恒定类型(immutable types)其实很简单,就是一但它们被创建,它们(的值) 就是固定的。如果你验证一些准备用于创建一个对象的参数,你知道它在验证状 态从前面的观点上看。你不能修改一个对象的内部状态使之成为无效的。在一个 对象被创建后,你必须自己小心翼翼的保护对象,否则你不得不做错误验证来禁 止改变任何状态。恒定类型天生就具有线程完全性的特点:多访问者可同时访问 相同的内容。如果内部状态不能修改,那么就不能给不同的线程提供查看不一致 的数据视图的机会。恒定类型可以从你的类上安全的暴露出来。调用者不能修改 对象的内部状态。恒定类型可以很好的在基于哈希代码的集合上工作。以 Object.GetHashCode()方法返回的值,对同一个实例是必须相同的(参见原则10) ,而这正是恒定类型总能成功的地方。
并不是所有的类型都能成为恒定 类型的。如果它可以,你需要克隆一个对象用于修改任何程序的状态了。这就是 为什么同时推荐使用恒定类型和原子类型数据了。把你的对象分解为自然的单一 实体结构。一个Address类型就是的,它就是一个简单的事,由多个相关的字段 组成。改变其中一个字段就很可能意味着修改了其它字段。一个客户类型不是一 个原子类型,一个客户类型可能包含很多小的信息块:地址,名字,一个或者多 个电话号码。任何一个互不关联的信息块都可以改变。一个客户可能会在不搬家 的情况下改变电话号码。而另一个客户可能在搬了家的情况下保留原来的电话号 码。还有可能,一个客户改变了他(她)的名字,而没有搬家也没有改电话号码。 一个客户类型就不是原子类型;它是由多个不同的恒定的组成部份构成的:地址 ,名字,以及一个成对出现的电话号码集合。原子类型是单一实体:你很自然的 用原子类型来取代实体内容。这一例外会改变它其中的一个组成字段。
下面就是一个典型的可变地址类的实现:
// Mutable Address structure.
public struct Address
{
private string _line1;
private string _line2;
private string _city;
private string _state;
private int _zipCode;
// Rely on the default system-generated
// constructor.
public string Line1
{
get { return _line1; }
set { _line1 = value; }
}
public string Line2
{
get { return _line2; }
set { _line2 = value; }
}
public string City
{
get { return _city; }
set { _city= value; }
}
public string State
{
get { return _state; }
set
{
ValidateState(value);
_state = value;
}
}
public int ZipCode
{
get { return _zipCode; }
set
{
ValidateZip( value );
_zipCode = value;
}
}
// other details omitted.
}
// Example usage:
Address a1 = new Address( );
a1.Line1 = "111 S. Main";
a1.City = "Anytown";
a1.State = "IL";
a1.ZipCode = 61111 ;
// Modify:
a1.City = "Ann Arbor"; // Zip, State invalid now.
a1.ZipCode = 48103; // State still invalid now.
a1.State = "MI"; // Now fine.