• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 13, 文章 - 0, 評(píng)論 - 3, 引用 - 0
            數(shù)據(jù)加載中……

            Equals方法的實(shí)現(xiàn)(參見《Microsoft.net框架程序設(shè)計(jì)》并提出少許建議)

            從《Microsoft.net框架程序設(shè)計(jì)》一書中,看到Equals的實(shí)現(xiàn)基本分為如下三類(順序有所調(diào)整):
            (1)引用類型,從MyRefType到Object的繼承鏈上(基類、基類的基類、...),有類覆蓋了Object的Equals方法實(shí)現(xiàn);
            (2)引用類型,從MyRefType到Object的繼承鏈上(基類、基類的基類、...),均沒有類覆蓋Object的Equals方法實(shí)現(xiàn);
            (3)值類型的Equals方法實(shí)現(xiàn)。
            分法相當(dāng)科學(xué),不過我看了其中的代碼實(shí)現(xiàn),針對(duì)第二種實(shí)現(xiàn)有一些自己的疑惑和想法。

            為了沒有看過該書的同仁們更好地理解,我將書中的實(shí)現(xiàn)貼在此文中。
            先看書中第一種,(1)引用類型,從MyRefType到Object的繼承鏈上(基類、基類的基類、...),有類覆蓋了Object的Equals方法的實(shí)現(xiàn):

             1//引用類型,從MyRefType到Object的繼承鏈上(基類、基類的基類、),有類覆蓋了Object的Equals方法
             2    class MyRefType : BaseType
             3    {
             4        RefType refobj;     //該字段是一個(gè)引用類型
             5        ValType valobj;     //該字段是一個(gè)值類型
             6
             7        public override bool Equals(object obj)
             8        {
             9            //首先讓基類比較其中的字段
            10            if (!base.Equals(obj))
            11                return false;
            12
            13            //因?yàn)閠his不是null,所以如果obj是null,那兩個(gè)對(duì)象將不可能相等
            14            if (obj == null)
            15                return false;
            16
            17            //如果類型不同,不可能相等
            18            if (this.GetType() != obj.GetType())
            19                return false;
            20            //將obj轉(zhuǎn)型為定義的類型以訪問其中的字段。
            21            //注意這里的轉(zhuǎn)型不會(huì)失敗,因?yàn)橐呀?jīng)知道兩個(gè)對(duì)象是同一個(gè)類型
            22            //obj的運(yùn)行時(shí)類型要么是MyRefType,要么是MyRefType的子類
            23            MyRefType other = (MyRefType)obj;
            24
            25            //比較其中的引用類型字段
            26            //這里調(diào)用Object的static 方法Equals,是為了兼容考慮refobj或者other.refobj為null的情況
            27            if (!Object.Equals(refobj, other.refobj))
            28                return false;
            29
            30            //比較其中的值類型字段,
            31            //因?yàn)橹殿愋筒豢赡転閚ull,所以直接調(diào)用值類型的Equals方法,免得裝箱的開銷
            32            if (!valobj.Equals(other.valobj))
            33                return false;
            34
            35            return true;    //到這里兩個(gè)對(duì)象才算相等
            36        }

            37    }

            其中用到了Object的Equals方法,將原代碼貼出來

             1    class Object
             2    {
             3        public virtual Boolean Equals(Object obj)
             4        {
             5            //如果兩個(gè)引用指向同一個(gè)對(duì)象
             6            //它們肯定相等
             7            if (this == obj)
             8                return true;
             9            return false;
            10        }

            11
            12        public static Boolean Equals(Object objA, Object objB)
            13        {
            14            //指向同一個(gè)對(duì)象,返回true
            15            if (objA == objB)
            16                return true;
            17
            18            //如果兩者任何一個(gè)為null,則不可能相等,返回false
            19            if ((objA == null||
            20                (objB == null))
            21                return false;
            22            
            23            //判斷objA和objB是否相等,返回比較結(jié)果
            24            return objA.Equals(objB);
            25        }

            26        
            27    }

            在這種實(shí)現(xiàn)中,個(gè)人認(rèn)為Equals方法是沒有問題的,首先調(diào)用

             //首先讓基類比較其中的字段
                        if (!base.Equals(obj))
                            
            return false;

            比較了基類的部分,然后就比較自己的部分就OK了。
            再看書中的第二種,2)引用類型,從MyRefType到Object的繼承鏈上(基類、基類的基類、...),均沒有類覆蓋Object的Equals方法的實(shí)現(xiàn);

             1//引用類型,從MyRefType到Object的繼承鏈上,均沒有類覆蓋Object的Equals方法
             2    class MyRefType : BaseType
             3    {
             4        RefType refobj;     //該字段是一個(gè)引用類型
             5        ValType valobj;     //該字段是一個(gè)值類型
             6
             7        public override bool Equals(object obj)
             8        {
             9            //因?yàn)閠his不是null,所以如果obj是null,那兩個(gè)對(duì)象將不可能相等
            10            if (obj == null)
            11                return false;
            12
            13            //如果類型不同,不可能相等
            14            if (this.GetType() != obj.GetType())
            15                return false;
            16            //將obj轉(zhuǎn)型為定義的類型以訪問其中的字段。
            17            //注意這里的轉(zhuǎn)型不會(huì)失敗,因?yàn)橐呀?jīng)知道兩個(gè)對(duì)象是同一個(gè)類型
            18            //obj的運(yùn)行時(shí)類型要么是MyRefType,要么是MyRefType的子類
            19            MyRefType other = (MyRefType)obj;
            20
            21            //比較其中的引用類型字段
            22            //這里調(diào)用Object的static 方法Equals,是為了兼容考慮refobj或者other.refobj為null的情況
            23            if (!Object.Equals(refobj, other.refobj))
            24                return false;
            25
            26            //比較其中的值類型字段,
            27            //因?yàn)橹殿愋筒豢赡転閚ull,所以直接調(diào)用值類型的Equals方法,免得裝箱的開銷
            28            if (!valobj.Equals(other.valobj))
            29                return false;
            30
            31            return true;    //到這里兩個(gè)對(duì)象才算相等
            32        }

            33    }

            我們可以發(fā)現(xiàn),同第一種實(shí)現(xiàn)的差別在于沒有了如下代碼段:

             //首先讓基類比較其中的字段
                        if (!base.Equals(obj))
                            
            return false;

            為什么不能加這一段呢?因?yàn)樵谶@種實(shí)現(xiàn)環(huán)境下,我們?cè)O(shè)定了前提:從MyRefType到Object的繼承鏈上,均沒有類覆蓋Object的Equals方法。所以如果加了上面這段代碼,那實(shí)際調(diào)用的是Object類的實(shí)例方法Equals

            1public virtual Boolean Equals(Object obj)
            2        {
            3            //如果兩個(gè)引用指向同一個(gè)對(duì)象
            4            //它們肯定相等
            5            if (this == obj)
            6                return true;
            7            return false;
            8        }

            這樣,如果this和obj不指向同一個(gè)對(duì)象,則這個(gè)base.Equals(obj)肯定返回false,于是Equals也返回false,而在this和obj指向同一個(gè)對(duì)象,Equals返回true,這樣不就是Object的Equals()實(shí)現(xiàn)嗎?既然如此,何苦自己重新這個(gè)函數(shù)呢?既然重寫了,那肯定有不一樣的行為,于是下面的代碼段加入不得。

             //首先讓基類比較其中的字段
                        if (!base.Equals(obj))
                            
            return false;


            到這里,我們也可以理解。不過問題是,沒有加入這段代碼,從“(2)引用類型,從MyRefType到Object的繼承鏈上(基類、基類的基類、...),均沒有類覆蓋Object的Equals方法的實(shí)現(xiàn)”中,我們好像找不到比較基類成員的影子。不比較基類成員,是否合理呢?

            看下面例子,如果兩個(gè)Derived對(duì)象,無需比較Base部分的成員變量,那么下面的寫法是正確的,此時(shí)該程序打印出的結(jié)果為True。

             1using System;
             2using System.Collections.Generic;
             3using System.Text;
             4
             5namespace MyTest
             6{
             7    //定義一個(gè)引用類型
             8    class MyRefType
             9    {
            10        public MyRefType(int value)
            11        {
            12            this.value = value;
            13        }

            14
            15        private int value;
            16    }

            17    //定義一個(gè)值類型
            18    struct MyValueType
            19    {
            20        public MyValueType(int value)
            21        {
            22            this.value = value;
            23        }

            24
            25        private int value;
            26    }

            27
            28    //基類
            29    class Base
            30    {
            31        //基類的引用類型成員,為了代碼簡(jiǎn)單,這里聲明為public
            32        public MyRefType baseRef;    
            33        //基類的值類型成員,為了代碼簡(jiǎn)單,這里聲明為public
            34        public MyValueType baseValue;   
            35    }

            36    
            37    //子類
            38    class Derived:Base
            39    {
            40        //子類的引用類型成員,為了代碼簡(jiǎn)單,這里聲明為public
            41        public MyRefType derivedRef; 
            42        //子類的引用類型成員,為了代碼簡(jiǎn)單,這里聲明為public
            43        public MyValueType derivedValue;
            44
            45        //這里Base沒有覆蓋Object的Equals()方法,所以這里采用Equals()的第二種實(shí)現(xiàn)方式
            46        //(2)引用類型,從MyRefType到Object的繼承鏈上,均沒有類覆蓋Object的Equals方法
            47        public override Boolean Equals(Object obj)
            48        {
            49            if (obj == null)
            50                return false;
            51            if (this.GetType() != obj.GetType())
            52                return false;
            53            Derived other = (Derived)obj;
            54
            55            if (!Object.Equals(this.derivedRef, other.derivedRef))
            56                return false;
            57
            58            if (!derivedValue.Equals(other.derivedValue))
            59                return false;
            60          
            61            return true;
            62        }
                    
            63    }

            64         
            65
            66    class Program
            67    {
            68        static void Main(string[] args)
            69        {
            70            MyRefType refType1 = new MyRefType(1);
            71            MyRefType refType2 = new MyRefType(2);
            72            MyRefType refType3 = new MyRefType(3);
            73
            74            Derived d1 = new Derived();
            75            Derived d2 = new Derived();
            76
            77            d1.derivedRef = refType1;
            78            d1.baseRef = refType2;
            79
            80            d2.derivedRef = refType1;
            81            d2.baseRef = refType3;
            82
            83            System.Console.Write(d1.Equals(d2));
            84            System.Console.Read();            
            85        }

            86    }
                
            87}

            88

            但是有時(shí)邏輯要求,兩個(gè)Derived對(duì)象需要比較Base部分的成員變量,那上面的寫法就是錯(cuò)誤的,此時(shí)應(yīng)該修改為如下的代碼,該程序打印出的結(jié)果為False。

             1using System;
             2using System.Collections.Generic;
             3using System.Text;
             4
             5namespace MyTest
             6{
             7    //定義一個(gè)引用類型
             8    class MyRefType
             9    {
            10        public MyRefType(int value)
            11        {
            12            this.value = value;
            13        }

            14
            15        private int value;
            16    }

            17    //定義一個(gè)值類型
            18    struct MyValueType
            19    {
            20        public MyValueType(int value)
            21        {
            22            this.value = value;
            23        }

            24
            25        private int value;
            26    }

            27
            28    //基類
            29    class Base
            30    {
            31        //基類的引用類型成員,為了代碼簡(jiǎn)單,這里聲明為public
            32        public MyRefType baseRef;    
            33        //基類的值類型成員,為了代碼簡(jiǎn)單,這里聲明為public
            34        public MyValueType baseValue;   
            35    }

            36    
            37    //子類
            38    class Derived:Base
            39    {
            40        //子類的引用類型成員,為了代碼簡(jiǎn)單,這里聲明為public
            41        public MyRefType derivedRef; 
            42        //子類的引用類型成員,為了代碼簡(jiǎn)單,這里聲明為public
            43        public MyValueType derivedValue;
            44
            45        //這里Base沒有覆蓋Object的Equals()方法,所以這里采用Equals()的第二種實(shí)現(xiàn)方式
            46        //(2)引用類型,從MyRefType到Object的繼承鏈上,均沒有類覆蓋Object的Equals方法
            47        public override Boolean Equals(Object obj)
            48        {
            49            if (obj == null)
            50                return false;
            51            if (this.GetType() != obj.GetType())
            52                return false;
            53            Derived other = (Derived)obj;
            54
            55            if (!Object.Equals(this.derivedRef, other.derivedRef))
            56                return false;
            57
            58            if (!derivedValue.Equals(other.derivedValue))
            59                return false;
            60
            61            ///////////////////////////////////////////////////////////
            62            //這里要加入比較基類的成員部分,具體比較什么視實(shí)際需要而定
            63            if (!Object.Equals(this.baseRef, other.baseRef))
            64                return false;
            65
            66            if (!baseValue.Equals(other.baseValue))
            67                return false;
            68            //////////////////////////////////////////////////////////
            69
            70            return true;
            71        }
                    
            72    }

            73         
            74
            75    class Program
            76    {
            77        static void Main(string[] args)
            78        {
            79            MyRefType refType1 = new MyRefType(1);
            80            MyRefType refType2 = new MyRefType(2);
            81            MyRefType refType3 = new MyRefType(3);
            82
            83            Derived d1 = new Derived();
            84            Derived d2 = new Derived();
            85
            86            d1.derivedRef = refType1;
            87            d1.baseRef = refType2;
            88
            89            d2.derivedRef = refType1;
            90            d2.baseRef = refType3;
            91
            92            System.Console.Write(d1.Equals(d2));
            93            System.Console.Read();            
            94        }

            95    }
                
            96}

            97


            再看第三種實(shí)現(xiàn):(3)值類型的Equals方法實(shí)現(xiàn)。書中代碼如下:

             1//值類型
             2    struct MyValType
             3    {
             4        RefType refobj;     //引用類型
             5        ValType valobj;     //值類型
             6
             7        public override bool Equals(object obj)
             8        {
             9            //這里不用GetType(),可以避免裝箱
            10            //同時(shí),因?yàn)橹殿愋筒荒苡凶宇?,所以這里用is就可以達(dá)到類型比較的目的
            11            if (!(obj is MyValType))
            12                return false;
            13            return this.Equals((MyValType)obj);
            14        }

            15
            16        public Boolean Equals(MyValType obj)
            17        {
            18            if (!Object.Equals(this.refobj, obj.refobj))
            19                return false;
            20            if (!this.valobj.Equals(obj.valobj))
            21                return false;
            22            return true;
            23        }

            24    }

            這種實(shí)現(xiàn)方法,如果不存在其它類對(duì)MyValType的隱式類型轉(zhuǎn)換,是沒有問題的,如果存在隱式類型轉(zhuǎn)換,那代碼中的強(qiáng)類型Equals()方法(從第16行開始)就可能存在問題了。試想想,兩種不同類型的實(shí)例,我們有多少場(chǎng)合會(huì)認(rèn)為他們Equals呢?根據(jù)邏輯來定,一般不會(huì)認(rèn)為它們相同。
            為了說明存在的這個(gè)問題,請(qǐng)看如下代碼:

             1 //值類型
             2    struct MyValType
             3    {
             4        private int value;     //值類型
             5
             6        public int Value
             7        {
             8            get return this.value; }
             9            set this.value = value; }
            10        }

            11
            12        public override bool Equals(object obj)
            13        {
            14            //這里不用GetType(),可以避免裝箱
            15            //同時(shí),因?yàn)橹殿愋筒荒苡凶宇悾赃@里用is就可以達(dá)到類型比較的目的
            16            if (!(obj is MyValType))
            17                return false;
            18            return this.Equals((MyValType)obj);
            19        }

            20
            21        public bool Equals(MyValType obj)
            22        {
            23            if (value != obj.Value)
            24                return false;
            25            return true;
            26        }

            27    }

            28
            29    //值類型
            30    struct MyValType2
            31    {
            32        private int value;     //值類型
            33
            34        public int Value
            35        {
            36            get return this.value; }
            37            set this.value = value; }
            38        }

            39
            40        public static implicit operator MyValType(MyValType2 obj)
            41        {
            42            MyValType myValType = new MyValType();
            43            myValType.Value = obj.Value;
            44            return myValType;
            45        }

            46    }

            47
            48    class Program
            49    {
            50        static void Main(string[] args)
            51        {
            52            MyValType myValue1 = new MyValType();
            53            myValue1.Value = 10;
            54
            55            MyValType2 myValue2 = new MyValType2();
            56            myValue2.Value = 10;
            57
            58            //這里會(huì)輸出True,從常理上說不應(yīng)該,從程序邏輯上就是True
            59            //程序先將myValue2隱式轉(zhuǎn)換為MyValType
            60            //然后調(diào)用函數(shù)public bool Equals(MyValType obj);
            61            System.Console.WriteLine(myValue1.Equals(myValue2));
            62            System.Console.Read();
            63        }

            64    }

            上面代碼中,一般從常理來說,我們不會(huì)認(rèn)為myValue1和myValue2相等,因?yàn)樗麄兪遣煌愋偷膶?shí)例(myValue1屬于MyValType類型,myValue2屬于MyValType2類型)。但是實(shí)際輸出了結(jié)果True。個(gè)中原因在于定義了一個(gè)從MyValType2到MyValType的隱式轉(zhuǎn)換:

            1public static implicit operator MyValType(MyValType2 obj)
            2        {
            3            MyValType myValType = new MyValType();
            4            myValType.Value = obj.Value;
            5            return myValType;
            6        }

            于是在運(yùn)行語句myValue1.Equals(myValue2)時(shí),會(huì)先將myValue2轉(zhuǎn)換為一個(gè)MyValType類型的臨時(shí)變量,然后用MyValue1和這個(gè)臨時(shí)變量比較,此時(shí)調(diào)用強(qiáng)類型的比較函數(shù),因?yàn)镸yValType和MyValType2這兩個(gè)類型內(nèi)部結(jié)構(gòu)一樣,且兩變量的內(nèi)部field的值相同,所以返回True。
            上面例子說明 ,如果不能確保沒有其他類型到該類型的隱式轉(zhuǎn)換,那萬無一失的辦法就是不實(shí)現(xiàn)強(qiáng)類型的Equals。調(diào)用默認(rèn)的Equals方法或者實(shí)現(xiàn)參數(shù)為Object的Equals方法,雖然效率可能差一點(diǎn),但是可靠。當(dāng)然,如果可以確認(rèn)沒有其他類型到該類型的隱式轉(zhuǎn)換,那實(shí)現(xiàn)強(qiáng)類型的Equals方法還是可以帶來效率的提升。(相同描述可參見《.net框架程序設(shè)計(jì)》)。

            關(guān)于Equals()方法的實(shí)現(xiàn),基本上也就到此結(jié)束了。如果各位有什么好的心得,歡迎積極探討。

            posted on 2009-04-26 23:33 五味雜陳 閱讀(1550) 評(píng)論(0)  編輯 收藏 引用 所屬分類: .NET

            国产精品女同久久久久电影院| 久久99精品久久久久久秒播| 91精品国产色综久久| 青青青国产成人久久111网站| 香港aa三级久久三级老师2021国产三级精品三级在 | 久久se精品一区精品二区| 日日噜噜夜夜狠狠久久丁香五月| 久久精品麻豆日日躁夜夜躁| 88久久精品无码一区二区毛片| 久久精品国产亚洲av瑜伽| 99久久精品国产免看国产一区| 亚洲日本va午夜中文字幕久久| 精品久久一区二区三区| 久久99国产精品成人欧美| 久久婷婷五月综合色奶水99啪| 久久99精品久久久久久久不卡 | 久久精品成人影院| 久久久无码精品亚洲日韩按摩| 中文精品久久久久国产网址| 伊人久久精品无码av一区| 久久久久亚洲AV无码专区网站 | 99国内精品久久久久久久| 久久久久亚洲AV成人网人人网站 | 久久无码人妻一区二区三区午夜| 精品久久久久久99人妻| 97久久精品人妻人人搡人人玩| 久久最新免费视频| 成人亚洲欧美久久久久| 大伊人青草狠狠久久| 热99RE久久精品这里都是精品免费 | 综合网日日天干夜夜久久| 久久国产高清一区二区三区| 69久久夜色精品国产69| 亚洲精品无码久久久久| 精品无码久久久久国产| 中文字幕久久精品无码| 久久久SS麻豆欧美国产日韩| 一本色道久久综合| 色婷婷综合久久久中文字幕| 久久久久波多野结衣高潮| 婷婷久久综合九色综合九七|