[C#] 類型學習筆記三:自定義值類型


既前兩篇之后,這一篇我們討論通過struct 關鍵字自定義值類型。

在第一篇已經討論過值類型的優勢,節省空間,不會觸發Gargage Collection等等。

在對性能要求比較高的場景下,通過struct代替類是不錯的選擇。

 

那么,比如我們定義一個Point 類型,里面包含兩個左邊X, Y。

    public struct Point
    {
        public int X;
        public int Y;
        public Point(int x, int y)
        {
            X = x;
            Y = y;
        }
    }

是不是這樣就OK了呢?

當然不是。因為我們必須盡量避免這個值類型被裝箱。

一個良好的值類型的定義,必須充分考慮到這個值類型的使用場景,然后定義好所需要的成員函數,從而避免有的函數調用不到而將值類型裝箱的情況。比如說,如果這個Point可能會被放到某個容器中,並且排序,那么Point就必須實現接口System.IComparable,實現CompareTo 方法和接口System.IComprable<T>中類型安全的CompareTo 方法。

老趙前輩的博文防止裝箱落實到底,只做一半也是失敗 給了一個非常好的例子,我把它引用過來做一點討論。

博文中的場景是struct所定義的值類型MyKey需要用作字典的鍵。

我們以System.Collections.Hashtable為例,其構造函數為

public Hashtable(
    int capacity,
    IEqualityComparer equalityComparer
)

這里面IEqualityComparer 是一個接口,用來作為HashTable的比較器。這個接口包含兩個函數:Equals 和 GetHashCode 。

(在System.Collections.Generic.Dictionary,以及其他一些集合的視線中,要求兩個對象為了相等,必須具有相同的哈希碼——CLR via  C# 第三版。所以在很多情況下,Equals 和 GetHashCode都是定義值類型必須重寫的兩個方法。)

當我們使用struct 定義的值類型來作為HashTable的key時,因為我們自定義的值類型中沒有提供Equals 和 GetHashCode的實現,因此值類型被裝箱,來調用ValueType的這兩個函數。

那么如何實現Equals 和 GetHashCode這兩個方法呢?

對於GetHashCode方法,我們使用 

public override int GetHashCode() {}

來重寫其內容,自定義的計算方式最好能夠做到返回的hash值能均勻分布。

對於equals 呢?如果我們僅僅用如下方法是不夠的

public override bool Equals(object that) {}

因為它提供的是和object類型的比較,我們真正需要的是和同樣類型MyKey的比較。

那么是否再加上這個就夠了?

public bool Equals(MyKey that) {}

確實差不多了,但是我們的MyKey需要指明是實現了哪一個接口。程序在運行時,這個接口中的Equals 方法因為在MyKey中被實現,所以才會直接調用MyKey中的 Equals方法。

原文中實現了IEquatable<MyKey>接口。

 

如果我們打開的Int32的定義看一看

namespace System
{public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<int>, IEquatable<int>
    {
               ........

        public override int GetHashCode()
        {
            return this;
        }

        public override bool Equals(object obj)
        {
            return obj is int && this == (int)obj;
        }

        public bool Equals(int obj)
        {
            return this == obj;
        }
                ....
     }
}

里面的Equals部分和 MyKey的定義一樣,也是有兩個實現。同時Int32 實現了IEquatable<int>接口。

 

相關閱讀

[C#] 類型學習筆記一:CLR中的類型,裝箱和拆箱

[C#] 類型學習筆記二:詳解對象之間的比較


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM