不变的原子值类型指的是那些其每一个属性可能因任何一个属性的改变而需改变,每个属性都不能独立更新的类型。
如:地址(地址中的邮政编码和地区省份是相关的,其中任何一个值的修改都会影响其他值,比如在北京100086这个邮政编码对应的是海淀区的某个地方,而100010对应的则是东城区某个地方)
对于这样的类型我们对其属性赋值时要格外小心,在修改某些属性的状态值的时候,由于相关属性的状态值没有修改会导致其产生非法的临时状态(例如将邮政编码从100086改为100010但未将海淀区改为东城区,这就产生了一个临时的非法状态),有很多的验证规则应在其属性的赋值访问器上,同时还要考虑到多线程和并发事务的问题。
这里有一个简单的做法就是总是用构造函数来创建新对象,而不直接赋值。如下面这个结构:
public struct Address3
{
// remaining details elided
public string Line1
{
get { return Line1; }
}
private readonly string line1;
public string Line2
{
get { return line2; }
}
private readonly string line2;
public string City
{
get { return city; }
}
private readonly string city;
public string State
{
get { return state; }
}
private readonly string state;
public int ZipCode
{
get { return zip; }
}
private readonly int zip;
public Address3(string line1,
string line2,
string city,
string state,
int zipCode) :
this()
{
this.line1 = line1;
this.line2 = line2;
this.city = city;
ValidateState(state);
this.state = state;
ValidateZip(zipCode);
this.zip = zipCode;
}
}
//这样使用这种类型
// Create an address:
Address2 a2 = new Address2("111 S. Main", "", "Anytown", "IL", 61111);
// To change, re-initialize:
a2 = new Address2(a1.Line1,a1.Line2, "Ann Arbor", "MI", 48103);
另外要注意的是不可变值类型中的可变类型的处理方式,这种类型多出现在引用类型的参数上,因为引用类型可以在类型外进行改变。代码如下:
// Almost immutable: there are holes that would
// allow state changes.
public struct PhoneList
{
private readonly Phone[] phones;
public PhoneList(Phone[] ph)
{
phones = ph;
}
public IEnumerable<Phone> Phones
{
get
{
return phones;
}
}
}
Phone[] phones = new Phone[10];
// initialize phones
PhoneList pl = new PhoneList(phones);
// Modify the phone list:
// also modifies the internals of the (supposedly)
// immutable object.
phones[5] = Phone.GeneratePhoneNumber();//注意这里phones 已经取得了PhoneList类型的phones字段的赋值权限。
为了防止这种行为,我们应该对这种在不可变类型中可变类型添加Copy赋值操作,以避免外界对其的引用修改。代码如下:
?
// Immutable: A copy is made at construction.
public struct PhoneList2
{
private readonly Phone[] phones;
public PhoneList2(Phone[] ph)
{
//这里创建了新数组,并且用CopyTo方法进行了赋值,防止了外界引用
phones = new Phone[ph.Length];
// Copies values because Phone is a value type.
ph.CopyTo(phones, 0);
}
public IEnumerable<Phone> Phones
{
get
{
return phones;
}
}
}
Phone[] phones2 = new Phone[10];
// initialize phones
PhoneList p2 = new PhoneList(phones);
此外还可以用工厂方法模式来创建不可变值对象如:.net Framework中Color类型的创建对象方式:
Color.FromKnownColor() and Color.FromName() //利用静态方法返回Color对象
最后,你还可以使用可变和不可变对象的组合的方式来实现这种控制,如StringBuilder(可变类型,执行可变行为),利用其ToString()方法,返回不可变类型String对象。