[英]C# Covariance on subclass return types
有誰知道為什么 C# 不支持協變返回類型? 即使在嘗試使用接口時,編譯器也會抱怨它是不允許的。 請參閱以下示例。
class Order
{
private Guid? _id;
private String _productName;
private double _price;
protected Order(Guid? id, String productName, double price)
{
_id = id;
_productName = productName;
_price = price;
}
protected class Builder : IBuilder<Order>
{
public Guid? Id { get; set; }
public String ProductName { get; set; }
public double Price { get; set; }
public virtual Order Build()
{
if(Id == null || ProductName == null || Price == null)
throw new InvalidOperationException("Missing required data!");
return new Order(Id, ProductName, Price);
}
}
}
class PastryOrder : Order
{
PastryOrder(Guid? id, String productName, double price, PastryType pastryType) : base(id, productName, price)
{
}
class PastryBuilder : Builder
{
public PastryType PastryType {get; set;}
public override PastryOrder Build()
{
if(PastryType == null) throw new InvalidOperationException("Missing data!");
return new PastryOrder(Id, ProductName, Price, PastryType);
}
}
}
interface IBuilder<in T>
{
T Build();
}
public enum PastryType
{
Cake,
Donut,
Cookie
}
感謝您的任何回復。
更新:這個答案是在 2011 年寫的。經過二十年的人們為 C# 提出返回類型協方差,看起來它最終會被實現; 我比較驚訝。 公告見https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/底部; 我相信細節會隨之而來。
首先,返回類型逆變沒有任何意義; 我想你在談論返回類型協方差。
有關詳細信息,請參閱此問題:
您想知道為什么未實現該功能。 phoog 是正確的; 該功能沒有實現,因為這里沒有人實現過它。 一個必要但不充分的要求是該功能的收益超過其成本。
費用相當可觀。 運行時本身不支持該功能,它直接違背了我們使 C# 可版本化的目標,因為它引入了另一種形式的脆弱基類問題,Anders 認為它不是一個有趣或有用的功能,如果你真的想要它,您可以通過編寫小助手方法使其工作。 (這正是 C++ 的 CIL 版本所做的。)
好處很小。
具有簡單解決方法的高成本、小收益功能很快就會被分類。 我們有更高的優先級。
逆變泛型參數不能輸出,因為不能保證在編譯時是安全的,C# 設計者決定不將必要的檢查延長到運行時。
這是簡短的答案,這里有一個稍長的答案......
方差是應用於類型層次結構的轉換的屬性:
在 C# 中,“轉換”是“用作泛型參數”。 例如,假設類Parent
由類Child
繼承。 讓我們將該事實表示為: Parent
> Child
(因為所有Child
實例也是Parent
實例,但不一定相反,因此Parent
是“更大”的)。 假設我們有一個通用接口I<T>
:
I<Parent>
> I<Child>
,則 T 是協變的(保留Parent
和Child
之間的原始“方向”)。I<Parent>
< I<Child>
,則 T 是逆變的(原始“方向”相反)。I<Parent>
與I<Child>
無關,則 T 是不變的。如果 C# 編譯器實際上同意編譯以下代碼...
class Parent {
}
class Child : Parent {
}
interface I<in T> {
T Get(); // Imagine this actually compiles.
}
class G<T> : I<T> where T : new() {
public T Get() {
return new T();
}
}
// ...
I<Child> g = new G<Parent>(); // OK since T is declared as contravariant, thus "reversing" the type hierarchy, as explained above.
Child child = g.Get(); // Yuck!
...這將導致運行時出現問題: Parent
被實例化並分配給對Child
的引用。 由於Parent
不是Child
,這是錯誤的!
最后一行在編譯時看起來沒問題,因為I<Child>.Get
被聲明為返回Child
,但我們不能在運行時完全“信任”它。 C# 設計者決定做正確的事情並在編譯時完全捕獲問題,並避免對運行時檢查的任何需要(與數組不同)。
(出於類似但“相反”的原因,協變通用參數不能用作輸入。)
Eric Lippert 在此站點上寫了一些關於方法覆蓋的返回方法協方差的帖子,但據我所知,沒有說明為什么該功能不受支持。 不過,他提到沒有計划支持它: https : //stackoverflow.com/a/4349584/385844
Eric 還喜歡說“為什么不支持X ”的答案總是一樣的:因為沒有人設計、實現和測試(等) X 。 一個例子在這里: https : //stackoverflow.com/a/1995706/385844
缺少此功能可能有一些哲學原因; 也許埃里克會看到這個問題並啟發我們。
編輯
正如普拉蒂克在評論中指出的那樣:
interface IBuilder<in T>
{
T Build();
}
應該
interface IBuilder<out T>
{
T Build();
}
這將允許您實現PastryOrder : IBuilder<PastryOrder>
,然后您就可以擁有
IBuilder<Order> builder = new PastryOrder();
可能有兩種或三種方法可以用來解決您的問題,但是,正如您所注意到的,返回方法協方差不是其中一種方法,並且這些信息都沒有回答為什么 C# 不支持它的問題。
只是為了將它發布到谷歌找到的地方......我正在研究這個,因為我想要一個接口,我可以在其中返回實現特定接口的任意類的集合/枚舉。
如果您可以定義要返回的具體類型,則可以簡單地相應地定義您的接口。 然后它將在編譯時檢查是否滿足約束(任何子類型)。
我提供了一個例子,可能對你有幫助。
正如 Branko Dimitrijevic 指出的那樣,通常允許協變返回類型通常是不安全的。 但是使用它,它是類型安全的,你甚至可以嵌套它(例如interface A<T, U> where T: B<U> where U : C
)
(免責聲明:我昨天開始使用 C#,所以我在最佳實踐方面可能完全錯誤,請有更多經驗的人對此發表評論:))
例子:
使用
interface IProvider<T, Coll> where T : ProvidedData where Coll : IEnumerable<T>
{
Coll GetData();
}
class XProvider : IProvider<X, List<X>>
{
List<X> GetData() { ... }
}
打電話
new XProvider().GetData
有效,在這種情況下是安全的。 在這種情況下,您只需定義要返回的類型。
更多相關信息: http : //msdn.microsoft.com/de-de/library/d5x73970.aspx
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.