关闭

转:《Effective C#》Item 7:推荐使用不可改变的原子值类型

433人阅读 评论(0) 收藏 举报

转:http://dev.csdn.net/author/Knight94/5582b364fc744be09bca2dca36786322.html

《Effective C#》Item 7:推荐使用不可改变的原子值类型

 

首先来解释一下标题,原标题为《Prefer Immutable Atomic Value Type》,因此对于标题的理解要分成三部分,第一部分为不可改变,第二部分为原子,最后一个部分为值类型CSDN也经常提到,例如保证操作的原子性之类的语句,那么一个原子类型,其的子成员为不可分割的一部分,不能单独被操作。。最后一部分,我不多说了,限制此章适用的范围。对于什么是不可改变类型,这里的意思是指此类型的变量一旦产生其成员就不能发生变化。至于原子类型,我以前在
 
听了标题解释,难免有些人会问,为什么要加上这样的限制,或者说这样做的好处是什么。为了解开这个疑团,我用一个例子来说明,去定义一个电话号码的值类型,一般形式如下:
    public struct Phone
    {
        private string strCountry_Code;
        private string strCity_Code;
        private string strPhone_Number;
        public string Country_Code
        {
            get{ return strCountry_Code; }
            set{ strCountry_Code = value;}
        }
        public string City_Code
        {
            get{ return strCity_Code; }
            set{ strCity_Code = value;}
        }
        public string Phone_Number
        {
            get{ return strPhone_Number; }
            set{ strPhone_Number = value;}
        }
 
        // Constructor
        public Phone( string sCountry_Code, string sCity_Code, string sPhone_Number )
        {
            strCountry_Code = sCountry_Code;
            strCity_Code = sCity_Code;
            strPhone_Number = sPhone_Number;
        }
    }
 
这样去初始化一个Phone类型变量的话,同时也可以做类似如下的相关操作。
    Phone myPhone = new Phone( "086", "010", "66666666" );
    myPhone.City_Code = "021";
    myPhone.Phone_Number = "777777777";
 
大多数人觉得如上的代码没有什么问题,不过稍微明眼的人看了如上的代码,就会立刻觉得有潜在的危险。作为一个Phone类型变量来说,国家区号,城市区号,以及电话号码来说是一个整体。因此动态修改其中的某一个值,会造成其他两个无效。也就是说如上的代码直接修改City_Code的时候,对于myPhone来说其他两个变量Country_Code以及Phone_Number来说,此时是无效的。不过这种错误在单线程中不是很明显,但是在多线程中是致命的,而且很难查出来。Lock语句或者互斥标识来避免。这样是可以避免,但是试问一下,类型是你创建的,你怎么要求别人在使用你这个类型的时候做过多的操纵呢,为什么你不在创建此类型的时候就直接把这条路堵死。如果明白了这一点,就理解了这篇文章推荐的目的,即与其后期增加代码弥补,不如在前期就编写正确的代码。有人可能会说了,在修改的时候加上
 
知道这样做的原因,接下来就是如何去实现。不过在实现之前,要区分什么样的数据类型可以定义成不可变的原子类型。也就是说,你如何区分一个类型是一个整体,而且每个分支不能独立于整体而存在。例如对于联系方式这个类型来说,它包括电话号码、住址等等。它可以看为一个整体,但是分支可以脱离这个整体而存在,因此它不是一个原子类型。对于如何具体区分,很难有一个统一的方法,毕竟适应的环境不同,操作以及实现也不同。不过对于原子类型,有一个唯一判断方式,就是每个分支能否独立于整体而被操作,这个的是与否决定是否为原子类型。
 
那么如何去定义一个不可变的原子值类型呢,大致要对原有的类型做两个处理,一个就是把所有成员加上readonly标示,即只能在构造函数中被修改;另一个就是删除属性set部分。对于Phone这个类型来说,经过处理后,正确的形式如下:
    public struct Phone
    {
        private readonly string strCountry_Code;
        private readonly string strCity_Code;
        private readonly string strPhone_Number;
        public string Country_Code
        {
            get{ return strCountry_Code; }
        }
        public string City_Code
        {
            get{ return strCity_Code; }
        }
        public string Phone_Number
        {
            get{ return strPhone_Number; }
        }
 
        // Constructor
        public Phone( string sCountry_Code, string sCity_Code, string sPhone_Number )
        {
            strCountry_Code = sCountry_Code;
            strCity_Code = sCity_Code;
            strPhone_Number = sPhone_Number;
        }
    }
 
这样对于一个Phone类型变量,只能通过new来创建(也就是说在输入三个有效的数据后,一个Phone类型变量才能产生)。
 
在此有人会问,除了new是否还有其他方法来进行修改。这是没有任何问题的,首先你只要理解了原子类型的意义,保证分支不会单独被修改即可,因此可以实现类似于“Phone.Parse”或者“Phone.From”之类的函数来形成一个新的Phone变量。
 
在实现不可变的原子值类型的时候,要防止类型中包括引用类型分支的时候,在进行成员赋值的时候,防止浅copy,这方面我就不多说了,参看我以前写的文章就可以明白(这里的目的也就是一点,防止局部破坏原子类型的分支)。

 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:111907次
    • 积分:1290
    • 等级:
    • 排名:千里之外
    • 原创:12篇
    • 转载:66篇
    • 译文:0篇
    • 评论:11条
    文章分类
    最新评论