参考自:http://www.cnblogs.com/fox23/archive/2008/07/26/understanding-immutable-in-csharp.html
有一种很简单也很受用的编程(不仅仅是C#)宗旨,就是所谓的"Immutability"(不可变性质)。如果一个类的实例是immutable的,那么我们把这个类也称作immutable class。
这样说来,似乎immutable的确是一个相当简单的东西,不过从以下几个问题中你可以找到使用immutable对象的便利之处。我们可以想一下,为什么编写一个多线程的应用程序要相对困难一些?那是因为在访问某些资源(对象或者其他OS掌管的资源)的时候线程间的同步问题总是会令人感到头疼。那为什么会有线程访问同步的问题呢?那是因为在多线程,多个对象之间,要保证他们的的多个读和写的操作不会引起冲突是一件很困难的事。那么这些冲突为什么会造成我们不希望的结果呢,其实关键就在于这里的“写”操作,因为只有它会改变对象的状态,给我们带来非预计的结果。
System.String
还是从这个“知名”的Immutable class开始谈吧,这个经常使用的类型被设计为immutable,当你改变一个String对象的时候,一个新的对象副本将被创建。尽管几乎所有的 C#教科书都会谈及这个问题,但是有时候我们似乎并不在意,于是我们经常会编写类似这样的语句:
string newstr = str.Replace("cn", "CN");
这里的str本身并没有改变,只是创建了一个"CNblogs"的副本。
很显然,对string频繁进行这样的操作会在内存中制造N多string对象,多数情况下那并不是我们所希望的。当然,这时候我们知道可以用System.Text.StringBuilder这样一个安全的方式来构造可变的字符串对象。
OK,上述内容几乎所有C#语言相关书籍上的说法都是一致的。但是String真的是完全immutable的么?
我想,这个倒未必哦,至少有这么几个方式是可以使得String不那么immutable的:
1. 直接操作指针
static unsafe void ToUpper( string str ) {
fixed ( char* pfixed = str )
for ( char* p = pfixed; *p != 0; p++ )
*p = char.ToUpper(*p);
}
static void Main() {
string str = "Hello";
System.Console.WriteLine(str);
ToUpper(str);
System.Console.WriteLine(str);
}
}
2. 使用反射
m_stringLength:This is the logical length of the string, the one returned by String.Length.
typeof(string).GetField("m_stringLength",
BindingFlags.NonPublic|BindingFlags.Instance).SetValue(s, 5);
从以上对String的讨论中我们至少可以得到以下几条immutable的优势
- 便于多线程编程
- 方便地作为hashtable的key
- 便于比较状态
不过我还是想提醒一下,immutable还是有副作用的,就比如之前提到的产生很多垃圾对象。
C#中immutable的实现
1. 经典的immutable class
{
public Contact(String fullName, String phoneNumber)
{
this.fullName= fullName;
this.phoneNumber= phoneNumber;
}
public Contact ChangeNumber(String newNumber)
{
//创建一个新实例
return new Contact (this.fullName, newNumber);
}
readonly String fullName;
public String FullName { get { return fullName; }}
readonly String phoneNumber;
public uint PhoneNumber{ get { return phoneNumber; }}
}
这个例子几乎无须再解释,每次changeNumber的时候就构造一个新的Contact对象。
C# 对immutability的支持离不开这两个关键字: const和readonly。
2. C#3.0中的immutable
C#3的编译器生成的匿名类型是immutable的,所有的字段都是private的,所有的属性都是只能get(使用reflector可以看到)。下面的代码将会在编译时报错:“Error 1 Property or indexer 'AnonymousType#1.A' cannot be assigned to -- it is read only ....”
{
var ab = new {A=1,B=2 };
ab.A = 3;
}