2010-03-23 20 views
5

的使用情況是一些什麼這樣的:爲什麼在實現接口的方法時,「協方差」和「逆變」的概念是適用的?

public class SomeClass: IClonable 
{ 
    // Some Code 

    //Implementing interface method 
    Public object Clone() 
     { 
     //Some Clonning Code 
     } 
} 

現在的問題是「爲什麼不能使用‘SomeClass的(因爲它是從objec derivd)’作爲克隆的返回類型()方法如果我們考慮到協變和逆變的豐達的。

有人可以解釋我這個implemementaion微軟????

+1

如果'IClonable'接口在您的代碼之前被寫入,那麼Clone方法如何返回特定類型?我們唯一的保證是,無論您是否執行克隆,都會從「對象」派生出來。由於您必須遵守確切的接口定義,因此您必須返回界面所說的內容,即「對象」。 – 2010-03-23 17:49:56

+0

在這裏,我想我Clone方法的實現應該能夠返回特定類型而不是IClonable的Clone()方法,當我實現IClonable接口時。但這是不允許的,我們得到一個編譯錯誤。所以我想知道原因,爲什麼協變或逆變的概念不允許實現一個接口。 – Amit 2010-03-24 04:13:33

回答

4

接口實現差異的非破壞實現必須在返回類型中協變,並且在參數類型中是相反的。

例如:

public interface IFoo 
{ 
    object Flurp(Array array); 
} 

public class GoodFoo : IFoo 
{ 
    public int Flurp(Array array) { ... } 
} 

public class NiceFoo : IFoo 
{ 
    public object Flurp(IEnumerable enumerable) { ... } 
} 

兩者都是在 「新」 規則的法律,對不對?但這又如何:

public class QuestionableFoo : IFoo 
{ 
    public double Flurp(Array array) { ... } 
    public object Flurp(IEnumerable enumerable) { ... } 
} 

很難分辨哪種隱式實現在這裏更好。第一個是參數類型的精確匹配,但不是返回類型。第二個是完全匹配的返回類型,但不是參數類型。我傾向於第一個,因爲使用接口的人只能給它一個Array,但它仍然不完全清楚。

到目前爲止這並不是最糟糕的。如果我們這樣做:

public class EvilFoo : IFoo 
{ 
    public object Flurp(ICollection collection) { ... } 
    public object Flurp(ICloneable cloneable) { ... } 
} 

哪一個獲獎?這是一個完全有效的過載,但是ICollectionICloneable與彼此無關,並且Array實現了它們兩者。我在這裏看不到一個明顯的解決方案。

它,只有當我們開始添加過載到接口本身變得更糟:

public interface ISuck 
{ 
    Stream Munge(ArrayList list); 
    Stream Munge(Hashtable ht); 
    string Munge(NameValueCollection nvc); 
    object Munge(IEnumerable enumerable); 
} 

public class HateHateHate : ISuck 
{ 
    public FileStream Munge(ICollection collection); 
    public NetworkStream Munge(IEnumerable enumerable); 
    public MemoryStream Munge(Hashtable ht); 
    public Stream Munge(ICloneable cloneable); 
    public object Munge(object o); 
    public Stream Munge(IDictionary dic); 
} 

好運試圖解開這個謎團沒有要瘋了。

當然,如果斷言接口實現應該只支持返回類型方差而不支持參數類型方差,那麼所有這些都是沒有意義的。但幾乎所有人都會認爲這樣的一個半實現被徹底打破並開始發送垃圾郵件報告,所以我不認爲C#團隊會這樣做。

我不知道這是爲什麼今天在C#中不支持它的官方原因,但它應該是它可能導致的那種「只寫」代碼的一個很好的例子,也是它的一部分C#團隊的設計理念是試圖阻止開發人員編寫糟糕的代碼。

+0

同意你的觀點Aaronaught。可能這就是微軟走這條路的原因。 – Amit 2010-03-24 04:39:32

+1

我不認爲大多數人會認爲這樣的半實施被打破。 C++支持返回類型協方差,但不支持形式參數類型的逆變。 Eiffel支持返回類型協方差和形式參數類型*協方差*,這很奇怪。 (艾菲爾有一種不尋常的分類方法。)Sather支持這兩種方法。因此,我沒有看到從C++到Sather的大規模遷移。 – 2010-03-26 00:07:00

+0

@Eric:我無法與其中任何一個爭論(雖然我可以說人們不會涌向Sather的原因是因爲他們從來沒有聽說過P:),但是用C#這樣的語言已經*已經*支持參數類型的變換作爲方法組轉換方差的一部分,我認爲缺乏該接口實現的功能肯定會感覺破碎;作爲一項規則,C#和.NET Framework很難保持一致。我的兩分錢! – Aaronaught 2010-03-26 00:29:08

2

你必須實現一個接口的方法,正是因爲他們在用戶界面背後的原因。ICloneable的克隆方法返回一個對象,所以你的SomeClass也必須返回一個對象呃,返回SomeClass的的克隆方法的SomeClass的情況下沒有任何問題,但該方法定義必須的接口相匹配:根據C#規範

public class SomeClass: IClonable 
{ 
    // Some Code 

    //Implementing interface method 
    Public object Clone() 
     { 
     SomeClass ret = new SomeClass(); 
     // copy date from this instance to ret 
     return ret; 
     } 
} 
+0

這是可以預料的。隨着新的.NET 4的東西在角落裏出現co/contra差異進出列表,是否有可能做到她正在問的是什麼? – 2010-03-23 17:54:51

0

,你必須重寫,或者實施時使用具有相同簽名的方法接口方法。請記住,Microsoft不擁有C#。他們的C#編譯器只是它們的實現。那麼爲什麼規範會這樣做?我只能猜測,但我懷疑這是爲了便於實施。

1

在解釋背後的C#決定的理由方面,從微軟埃裏克利珀寫了很多解釋C#魂鬥羅/協方差......這裏是從他的博客標籤列表: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

[編輯] 具體到您的問題,這可能是正確的職位.. http://blogs.msdn.com/ericlippert/archive/2007/10/26/covariance-and-contravariance-in-c-part-five-interface-variance.aspx

+0

不完全。這些文章是關於參數化類型差異。問題是關於返回類型方差,這是不同的。 – 2010-03-25 23:49:49

0

它看起來像他們可以使用泛型的東西,但它似乎有一個很好的理由,爲什麼他們沒有。

它講到這裏:

http://bytes.com/topic/c-sharp/answers/469671-generic-icloneable

基本上,一個通用的接口,將允許: public class MyClass : IClonable<MyClass>

也將使: public class MyClass : IClonable<MyOtherClass>

它並沒有真正提供任何好處,並可能混淆事物。

+0

我會假設IClonable 將是一個非常好的模式,尤其是如果它繼承了ISelf ,而這又包含T類型的只讀「自我」屬性。在許多情況下,我會刪除暴露公共克隆方法的類型應該不可繼承,但應具有除具有受保護的克隆方法之外的基本類型。通過這種方式,「foo」和「derivedFoo」都可以具有不可克隆的衍生物,但可以編寫一個例程來接受CloneableFoo和CloneableDerivedFoo(因爲兩者都是ICloneable )。 – supercat 2011-08-24 18:30:51

8

讓我改一下這個問題:作爲

這樣的語言C++允許壓倒一切的方法有一個更具體的返回類型比重寫的方法。例如,如果我們有類型

abstract class Enclosure {} 
class Aquarium : Enclosure {} 
abstract class Animal 
{ 
    public virtual Enclosure GetEnclosure(); 
} 

那麼這是不合法的C#,但相當的代碼將在C法律++:

class Fish : Animal 
{ 
    public override Aquarium GetEnclosure() { ... 

這是什麼功能的C++調用?

該功能被稱爲「返回類型協方差」。 (作爲另一個答案指出,它也將有可能支持「正式參數類型逆變」,雖然C++不。)

爲什麼不是在C#支持?

正如我多次指出的那樣,我們不必提供不支持某個功能的原因;所有功能的默認狀態是「不支持」。只有當大量的時間和精力投入到實現中才能支持某項功能。相反,功能實施必須有他們的理由,並考慮到它的成本多少是很好的理由。

這就是說,有兩個「反對」這個特徵的主要原因是阻止它完成。

  1. CLR不支持它。爲了做到這一點,我們基本上必須實現完全匹配的方法,然後創建一個調用它的幫助器方法。這是可行的,但它會變得混亂。

  2. 安德斯認爲它不是一個很好的語言功能。安德斯是首席建築師,如果他認爲這是一個壞主題,那麼勝算是好事,它不會完成。 (現在,請注意,我們認爲命名參數和可選參數也不值得花錢,但最終還是可以完成的。有時很明顯,您必須咬緊牙關,實現一個您不太喜歡的功能爲了滿足現實世界的需求)。

總之,當然有時候它會很有用,而且這是一個頻繁要求的功能。但是,我們不太可能會這樣做。該功能的好處不會支付其成本;它使方法的語義分析變得相當複雜,我們並沒有真正簡單的方法來實現它。

+2

所以,明顯的後續問題:*「爲什麼安德斯認爲返回類型協方差是一個壞的特點?」* – LBushkin 2010-03-26 14:07:04

+1

@Lushushkin:下次見到他時,問問他。 :-) – 2010-03-26 14:16:08

相關問題