对象的常量性和原子性

  • 对象的原子性:对象的状态是一个整体,如果一个字段改变,其他字段也要同时做出 相应改变。简单来说,就是要么不改,要么全改。
    解决办法:实施的办法是添加一个构造函数,在这个构造函数中为对象的所有字段赋值。

  • 对象的常量性:对象的状态一旦确定,就不能再次更改了。如果想再次更改,需要重新构造一个对象。
    解决办法:不允许在为对象赋值以后还能对对象状态进行修改,所以将属性中的set访问器删除,同时将字段声明为readonly:

  • 上面的方法解决了数据不一致的问题,但是还漏掉了一点:当类型内部维护着一个引用
    类型字段时,比如数组,尽管将它声明为readonly,在类型外部还是可以对它进行修改。现在修改Address类,为它添加一个数组phones,存储电话号码:

   private readonly string[] phones;
   public Address(string province, string city, string zip, string[] phones) 
   { // 略... this.phones = phones; } 
   public string[] Phones {}

可以看到,尽管将phones字段声明为了readonly,并且它也只提供了get属性访问器。
仍然可以通过Address对象a外部的变量b,修改了a对象内部的内容。如何避免这种情况的发
生呢?可以通过本节上面介绍的深度复制的方式来解决,在Phones的get属性访问器中添加
如下代码:

public string[] Phones {
get {
string[] rtn = new string[phones.Length];
phones.CopyTo(rtn, 0);
return rtn;
}}
避免外部类型对类型内部的访问
  • 上面的方法解决了数据不一致的问题,但是还漏掉了一点:当类型内部维护着一个引用
    类型字段时,比如数组,尽管将它声明为readonly,在类型外部还是可以对它进行修改。现
    在修改Address类,为它添加一个数组phones,存储电话号码:
private readonly string[] phones;
public Address(string province, string city, string zip, string[] phones) {
// 略...
this.phones = phones;
} 
public string[] Phones {
get { return phones; }
}

可以看到,尽管将phones字段声明为了readonly,并且它也只提供了get属性访问器。
仍然可以通过Address对象a外部的变量b,修改了a对象内部的内容。如何避免这种情况的发
生呢?可以通过本节上面介绍的深度复制的方式来解决,在Phones的get属性访问器中添加
如下代码:

public string[] Phones {
get {
string[] rtn = new string[phones.Length];
phones.CopyTo(rtn, 0);
return rtn;
}
}

在get访问器中,创建了一个新的数组,并将Address对象本身的数组内容进行了复制,
然后返回给调用者。此时,再次运行刚才的代码,由于b指向了新创建的这个数组对象,而
非Address对象a内部的数组对象,所以对b的修改将不再影响到a。再次运行刚才的代码,可
以得到029-88401100的输出。
但是问题还没有结束,再看下面这段代码:

string[] phones = { "029-88401100", "029-88500321" };
Address a = new Address("陕西", "西安", "710068", phones);
Console.WriteLine(a.Phones[0]); // 输出: 029-88401100
phones[0] = "029-XXXXXXXX"; // 通过phones变量修改了Address对象内部的数据
Console.WriteLine(a.Phones[0]); // 输出: 029-XXXXXXXX

在创建完Address对象后,依然可以通过之前的数组变量来修改对象内部的数据,受到
前面的启发,很容易想到可以在构造函数中对外部传递进来的数组进行深度复制:

public Address(string province, string city, string zip, string[] phones) {
// 前面略...
this.phones = new string[phones.Length];
phones.CopyTo(this.phones, 0);
CheckZip(zip); // 验证格式
}

这样,再次运行上面的代码,对于phones的修改便不会再影响到Address对象本身。

猜你喜欢

转载自blog.csdn.net/qq_27445903/article/details/79338422