Readonly and Mutable, vs. Read / Write Properties

刚刚逛Bill Wanger的blog看到了一篇帖子,虽然感觉没什么新意,不过既然他老人家郑重其事地提出来了,估计有他的用意,自己写了个程序验证了一下,贴在下面,由于他老人家用的是C#2.0(用到了范型),俺机子没有装,只能把它翻译成C#1.1的版本了.
class ClassPrint
    
{
        
private readonly ArrayList words;
        
public ClassPrint(ArrayList _words)
        
{
            words 
= _words;
        }

        
        
public void printTheWords()
        
{
            
foreach(object _word in words)
            
{
                Console.WriteLine(_word.ToString());
                Console.WriteLine();
            }

            Console.ReadLine();
        }

        
static void Main(string[] args)
        
{
            ClassWords testWords 
= new ClassWords();
            ClassPrint testPrint 
= new ClassPrint(testWords._pWords);
            
//The First Example
            testWords._pWords.Add("The First Example:\n");
            testWords._pWords.Add(
"I become a uncle today!");
            testPrint.printTheWords();

            
//The Second Example
            testWords._pWords = new ArrayList();
            testWords._pWords.Add(
"The Second Example:\n");
            testWords._pWords.Add(
"I am very happy now!");
            testPrint.printTheWords();

        }

        
    }


    
class ClassWords
    
{
        
private ArrayList words = new ArrayList();
        
public ArrayList _pWords
        
{
            
get
            
{
                
return words;
            }

            
set
            
{
                words 
= value;
            }

        }

    }

代码很简单,随便看看就懂了.
Bill Wanger的意思是如果不通过编译器仅仅通过大脑去思考的话,很多程序员会以为是如下输出:
The First Example:
I become a uncle today!
The Second Example:
I am very happy now!
实际的输出应该为
The First Example:
I become a uncle today!
The First Example:
I become a uncle today!

这个其实很好理解,负责输出的classPrint的words是readonly的只能在声明或者构造函数中为其赋值,这个字段在构造函数中被指定为一块内存区域,就不会再做更改.当然这个地方的words字段即使不是readonly的输出结果还是一样的,因为它指向的内存区域跟_pWords已经不一样的,_pWords已经指向了另一段内存区域,它的更改并不会影响到words.
上面扯了半天好像离Bill Wanger的意思越来越远了,哈哈.还是说一下他的意思吧.
Bill Wanger的意思是,下面的定义

private ArrayList words = new ArrayList();
        
public ArrayList _pWords
        
{
            
get
            
{
                
return words;
            }

        }

叫做易变(Mutable)只读属性,可以改变该属性的状态,但不能替换它,比如可以用Add(),Remove(),Clear()方法来改变ArrayList属性的状态,但不能让其指向另一个ArrayList.
但是

private ArrayList words = new ArrayList();
        
public ArrayList _pWords
        
{
            
get
            
{
                
return words;
            }

            
set
            
{
                words 
= value;
            }

        }

却可以让它指向另一个ArrayList,这样如果一个客户端代码已经缓存了这个属性原始数据的引用,如果用set方法让它指向新的ArrayList就会显得很突然和奇怪.
我觉得这其实是很正常和比较好理解的,如果类在设计的时候不允许它再指向新的ArrayList(当然这里的Mutable,可以指任何引用类型的数据,比如DataSet等等),那就把这个字段设计为只读,如果所有的实例只允许一份值拷贝,干脆用Sington Pattern.
Bill Wanger写的<<Effective C#>>和<<The C# Core Language Little Black Book>>据说很经典,等看完Anders就可以看这两本了,爽.


原文地址:https://www.cnblogs.com/Farseer1215/p/280808.html