2011-11-08 93 views
7

是否可以實現受限於兩個唯一泛型參數的類?具有兩個不相等(唯一)類型的泛型類

如果不是,那是因爲它沒有實現,或者因爲語言結構(繼承)而不可能?

我想形式的東西:

class BidirectionalMap<T1,T2> where T1 != T2 
{ 
    ... 
} 

我實施Bidirectional dictionary。這主要是好奇心問題,而不是需要。


意譯從註釋:

  1. 丹:「什麼是負面後果,如果這種限制不符合」

  2. 我:「那麼用戶可以使用map [t1]和map [t2]來索引,如果它們是相同的類型,那麼就沒有區別,這是沒有任何意義的。丹:編譯器實際上允許[兩個泛型類型參數定義不同的方法重載],所以我很好奇; 它是否隨意挑選其中一個方法來調用?

+1

您可以在構造函數中始終拋出一個'ShouldBeBeCompileTimeError()' 。 :-) – foson

+1

可能是因爲它沒有意義,它們是泛型,不應該被類型「約束」。 – GriffinHeart

+0

出於好奇,如果不滿足這個約束,會有什麼負面影響?來自同一組的對象之間的映射是非常普遍的要求。 –

回答

4

擴大的例子突出問題:

public class BidirectionalMap<T1,T2> 
{ 
    public void Remove(T1 item) {} 
    public void Remove(T2 item) {} 

    public static void Test() 
    { 
     //This line compiles 
     var possiblyBad = new BidirectionalMap<string, string>(); 

     //This causes the compiler to fail with an ambiguous invocation 
     possiblyBad.Remove("Something"); 
    } 
} 

因此,答案是,即使你不能指定約束T1 = T2,也不要緊,因爲編譯器只要你試圖做一些違反隱含約束的事情就會失敗。它仍然在編譯時捕獲失敗,所以你可以使用這些重載而不受懲罰。這有點奇怪,因爲你可以創建一個地圖實例(甚至可以編寫IL代碼來適當地操作地圖),但C#編譯器不會讓你通過任意解決模糊的重載而造成破壞。


一方面要注意的是,如果你不小心,這種重載可能會導致一些奇怪的行爲。如果你有一個BidirectionalMap<Animal, Cat>和貓:動物,可以考慮將與此代碼發生的事情:

Animal animal = new Cat(); 
map.Remove(animal); 

這將調用了一個動物的過載,所以它會嘗試刪除鍵,即使你可能已經打算刪除值Cat。這是一個有些人爲的情況,但是當由於方法重載而出現非常不同的行爲時,請謹慎行事。在這種情況下,如果您只是給方法不同的名稱,反映它們的不同行爲(比如RemoveKey和RemoveValue,假設),則讀取和維護可能會更容易。

+0

優秀的根本答案!錯誤文本:'在以下方法或屬性之間調用不明確:BidirectionalMap .this [T]'和'BidirectionalMap .this [K]'「 – user664939

0

類型約束似乎是不恰當的。雖然它們約束了類型參數,但目的是讓編譯器知道該類型可用的操作。
如果你想,你可以有一個約束,其中T1和T2都來自單獨的具體基類,但我不認爲這就是你想要的。

-1

沒有任何有效的方法來做到這一點,而不會對類型本身施加任何其他限制。正如其他人指出的那樣,您可以對這兩種類型從兩個不同的基類派生出來進行約束,但從設計的角度來看,這可能不是很好。

編輯補充說:這個沒有實現的原因很可能是因爲微軟沒有人考慮過這樣的事情在編譯時需要強制執行,與其他約束條件不同,這些約束條件與您的實際能力有什麼關係使用指定類型的變量。正如一些評論者指出的那樣,您可以在運行時強制執行此操作。

0

不等式不會幫助編譯器捕獲錯誤。當你指定對類型參數的約束時,你告訴編譯器這種類型的變量將總是支持某個接口,或者將以某種方式運行。其中每一個都允許編譯器驗證更多的東西,比如「這個方法將會出現,所以它可以在T上調用」。

類型參數的不等式更像是驗證方法參數不爲null。它是程序邏輯的一部分,而不是其類型安全。

0

我不完全確定爲什麼這將是一個理想的編譯時檢查。基本上可以通過裝箱鍵或值來繞過條件,從而使編譯時檢查無效。

需要考慮一些因素來確定......我試圖防止哪些錯誤?

如果您只是停止不閱讀文檔的懶惰同事,然後添加一個僅調試檢查並引發異常。通過這種方式,可以刪除檢查以獲得釋放代碼。

#if Debug 

if (T1 is T2 || T2 is T1) 
{ 
    throw new ArguementException(...); 
} 

#endif 

如果你正試圖阻止惡意的人利用以無意的方式庫中,那麼也許需要一個運行時檢查,否則會很容易框中鍵或值。

+0

您也可以將此檢查放在泛型類型的靜態構造函數中,所以它只會運行一次(順便說一下,你的檢查不會被編譯,因爲'is'運算符的左邊是一個對象引用,而不是一個類型名稱,你需要if(typeof(T1)= = typeof(t2)') – phoog

0

不,你不能使用等式(或不等式)作爲約束。簡而言之,平等不是約束,而是一種條件。您應該測試條件,例如構造函數中類型的相等或不相等,並引發適當的異常。

相關問題