2009-08-09 77 views
2

我想知道爲什麼C#團隊決定不支持非泛型的共同/反變換,因爲他們可能同樣安全。這個問題相當主觀,因爲我不希望有團隊成員迴應,但有人可能有我(和芭芭拉麗斯科夫)缺乏的洞察力。對非泛型類型的協變/逆變支持?

讓我們看看這個樣本接口:

public interface ITest 
{ 
    object Property 
    { 
     get; 
    } 
} 

下面的實現會失敗,雖然完全安全的(我們總是可以返回一個更具體的類型不違反界面 - 而不是在C#中,但至少在理論上)。

public class Test : ITest 
{ 
    public string Property 
    { 
     get; 
    } 
} 

代碼自然不會是安全的,如果接口包括一個二傳手,但是這是沒有理由的限制實現全面,因爲這可以通過/使用了申報安全指出,正如對仿製藥。

+0

當然,這個實現也會失敗,因爲get沒有主體,但是對於這個簡單的例子,請忽略這個事實;-) – 2009-08-09 11:22:05

+1

Dup:http://stackoverflow.com/questions/837134/why-does-c-clr -not-support-method-override-co-contra-variance – thecoop 2009-08-09 17:27:26

+0

thecoop,我發現自己,所以我已經給我的投票結束這個問題。 – 2009-08-09 18:29:44

回答

2

CLR不支持協變返回類型,而它支持.NET 2.0以上的委託/接口通用差異。

換句話說,它不是真的取決於C#團隊,而是CLR團隊。

至於爲什麼CLR不支持正常的方差 - 除了增加複雜度外,我不確定,大概沒有必要的感知效益。

編輯:爲了對抗約返回類型協方差的點,從CLI規範的部分8.10.4,談到虛表「槽」:

對於被標記 「期望現有時隙中的每個新的構件「,查看 是否有完全匹配的種類(即字段或 方法),名稱和簽名是否存在 並使用該槽如果找到,則返回 否則分配新槽。

從分區II,第9.9節:

具體地,爲了確定 是否構件獸皮(靜態或 實例成員)或覆蓋(對於 虛擬方法)從一個構件基類 類或接口,只需用 替換每個泛型參數與它的 一般參數,然後比較產生的成員簽名即 。

沒有跡象表明比較是以允許差異的方式完成的。

如果您認爲CLR 確實允許允許有差異,我認爲考慮到上面的證據,您可以用一些適當的IL證明它。

編輯:我只是試圖它在IL,它不起作用。編譯此代碼:

using System; 

public class Base 
{ 
    public virtual object Foo() 
    { 
     Console.WriteLine("Base.Foo"); 
     return null; 
    } 
} 

public class Derived : Base 
{ 
    public override object Foo() 
    { 
     Console.WriteLine("Derived.Foo"); 
     return null; 
    } 
} 

class Test 
{ 
    static void Main() 
    { 
     Base b = new Derived(); 
     b.Foo(); 
    } 
} 

運行它,與輸出:

Derived.Foo 

拆解:

ildasm Test.exe /out:Test.il 

編輯Derived.Foo擁有的 「串」 的返回類型,而不是 「對象」 :

.method public hidebysig virtual instance string Foo() cil managed 

R的ebuild:

ilasm /OUTPUT:Test.exe Test.il 

重新運行它,與輸出:

Base.Foo 

換句話說,Derived.Foo不再跨越Base.Foo至於CLR而言。

+0

「方法簽名定義了調用約定,方法的參數類型和方法的返回類型「(來自ECMA-335,8.11.1)。 由於CLR是CLI的一個實現,是否它不支持返回類型重載 - 從而允許差異? – 2009-08-09 12:57:01

+0

不,因爲支持返回類型重載*與支持方差不同。我會在* overriding *上查找相關位,這是重要的部分。 – 2009-08-09 13:11:32

+0

這可以在C#編譯器中解決,通過向程序集添加額外的元數據並在正確的位置插入轉換(例如java如何處理泛型 - 類型擦除+投射) – thecoop 2009-08-09 17:26:09

1
  1. 方法的返回值總是「out」,它們總是在賦值表達式的右側。
  2. CLR彙編格式具有將實例方法分配給接口方法的元數據,但此方法推斷僅依賴於輸入參數,它們可能需要創建新的格式以支持,而不僅僅是它也變得模糊。
  3. 實例方法和接口簽名之間的新方法映射算法也可能很複雜且CPU密集型。我相信接口方法的方法簽名在編譯時解決。因爲在運行時它可能太昂貴了。
  4. 方法推理/解決可能是問題,如下所述。

考慮以下與唯一不同的返回類型允許的動態方法解析(這是絕對錯誤的)

public class Test{ 
    public int DoMethod(){ return 2; } 
    public string DoMethod() { return "Name"; } 
} 
Test t; 
int n = t.DoMethod(); // 1st method 
string txt = t.DoMethod(); // 2nd method 
object x = t.DoMethod(); // DOOMED ... which one?? 
+0

如果您有一個實現兩個接口並將實例傳遞給兩個接口都有重載的方法的類,則現在已存在此問題。 – 2009-08-09 12:45:54

+0

就像我對CTS中的Jon Skeet所說的,方法簽名包含了返回類型。 – 2009-08-09 12:57:55

1

的CLR不支持方法覆蓋方差樣本,但對於接口實現一種解決方法:

public class Test : ITest 
{ 
    public string Property 
    { 
     get; 
    } 

    object ITest.Property 
    { 
     get 
     { 
      return Property; 
     } 
    } 
} 

這將實現爲協覆蓋同樣的效果,但只能用於接口和用於直接實現

+0

這就是我最終解決它的方法,但隨着更多的類使用類似的方法,代碼變得很難看。我想我沒有任何選擇。 – 2009-08-09 18:32:37