2014-09-24 19 views
0

我想創建一個結構,在構造過程中需要可變數量的參數,意圖將該對象用作字典鍵(我的Tuple類型不受支持的.NET版本):C#結構字典鍵不能與陣列成員一起工作

struct TupleKey 
{ 
    int[] args; 
    public TupleKey(params int[] args) { this.args = args; } 
} 

然而,當我使用這個struct作爲字典的鍵,ContainsKey方法返回false。

var d = new Dictionary<TupleKey, int>(); 
d.Add(new TupleKey(1, 1), 1); 
Console.WriteLine(d.ContainsKey(new TupleKey(1,1))); // false!? 

發生了什麼事?在結構中使用可變對象(如數組)時是否存在問題?

+0

因爲它的動態數組需要指定它的大小。 – ZoomVirus 2014-09-24 15:05:24

+1

@karlsweeney不,根本不是這種情況。 – Servy 2014-09-24 15:09:38

回答

5

自定義struct的默認相等和哈希代碼實現將基於其成員的默認相等和哈希代碼方法,在您的情況下是一個數組。該數組使用基於引用的標識,而不是基於值的標識。如果你想讓不同的數組具有相同的值,那麼你需要重寫EqualsGetHashCode以取決於數組的值。

+0

這是有道理的。我曾經想過'Struct'是通過值比較而不是通過引用來比較的(當成員是引用類型時,這顯然不是真實的)。謝謝Servy。 – rookie 2014-09-24 15:11:41

+0

@gjdanis結構* *正在通過價值進行比較。在這種情況下,struct *的值是一個引用*。 – Servy 2014-09-24 15:12:54

0

一般來說,在.NET中的類型嘗試定義Equals這樣,如果xy是一類的私人領域的x.Equals(y)值不能改變,除非類寫入到這些領域。如果xy是可變參考類型,這意味着唯一的方法x.Equals(y)可能是真的,如果xy標識相同的對象。如果x.Equals(y)xy識別出狀態恰好相同的不同對象時返回true,並且在其他地方的代碼中引用其中一個對象的代碼將修改其狀態,則該外部代碼可能會更改x.Equals(y)的值而不訪問到xy

我認爲可以合理地爭辯說,.NET的缺乏「不可變陣列」類型;如果存在這樣的類型,那麼它們是不變的,可以保證如果兩個實例包含相同的項目,它們將永遠這樣做。因爲.NET沒有這種類型,所以有必要忍受這個限制。

要做的最好的事情可能是讓你的結構的構造函數構造一個數組,它比傳入數組長一個元素,並且包含原始數組內容的副本以及其中的值的散列。這個數組永遠不會暴露給外部代碼,因此可以保證永遠不會被修改。你的equals方法可以檢查你的類型被比較的東西是否是另一個相同類型的結構,如果是的話,檢查數組是否長度相同,存儲的哈希值是否匹配,如果是,是否所有其他項目匹配。您的GetHashCode值應該返回存儲在額外數組插槽中的散列值。如果你這樣做,你也應該執行IEquatable<yourOwnType>

請注意,儘管可以使用字段而不是陣列插槽來保存散列值,但使用陣列插槽會更有效,並且可以避免不恰當的多線程代碼可能會創建結構其哈希值與數組內容不一致的實例。