有谁知道为什么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
}
感谢您的回复。
答案 0 :(得分:25)
首先,返回类型逆转没有任何意义;我想你在谈论返回类型协方差。
有关详细信息,请参阅此问题:
Does C# support return type covariance?
您想知道为什么没有实现该功能。 phoog是正确的;该功能未实现,因为此处没有人实现它。必要但不充分的要求是该功能的好处超过其成本。
成本相当可观。运行时本身不支持该功能,它直接影响我们使C#版本化的目标,因为它引入了另一种形式的脆弱基类问题,Anders认为它不是一个有趣或有用的功能,如果你真的想要它,你可以通过编写小帮助方法使它工作。 (这正是C ++的CIL版本所做的。)
好处很小。
高成本,小型优势功能以及简单的解决方法可以非常快速地将其分类。我们有更高的优先级。
答案 1 :(得分:9)
不能输出逆变通用参数,因为在编译时不能保证安全,C#设计者决定不将必要的检查延长到运行时间。
这是简短的答案,这里稍长一点......
Variance是应用于类型层次结构的转换的属性:
在C#中,“转换”是“用作通用参数”。例如,假设类Parent
由类Child
继承。我们将这个事实表示为:Parent
&gt; Child
(因为所有Child
个实例也是Parent
个实例,但不一定相反,因此Parent
是“更大”)。我们还说我们有一个通用接口I<T>
:
I<Parent>
&gt; I<Child>
,T是协变的(保留Parent
和Child
之间的原始“方向”。I<Parent>
&lt; 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#设计者决定做正确的事情并在编译时完全解决问题,并避免任何运行时检查的需要(与数组不同)。
(出于类似但“反向”的原因,协变通用参数不能用作输入。)
答案 2 :(得分:7)
Eric Lippert在此网站上撰写了一些关于方法覆盖的返回方法协方差的帖子,但我没有看到为什么该功能不受支持。但他提到,没有计划支持它:https://stackoverflow.com/a/4349584/385844
Eric还喜欢说“为什么不支持 X ”的答案总是一样的:因为没有人设计,实现和测试(等等) X 即可。这方面的一个例子是:https://stackoverflow.com/a/1995706/385844缺乏此功能可能有一些哲学原因;也许埃里克会看到这个问题并启发我们。
修改强>
Pratik在评论中指出:
interface IBuilder<in T>
{
T Build();
}
应该是
interface IBuilder<out T>
{
T Build();
}
这将允许您实施PastryOrder : IBuilder<PastryOrder>
,然后您可以
IBuilder<Order> builder = new PastryOrder();
您可以使用两种或三种方法来解决问题,但是,正如您所指出的,返回方法协方差不是这些方法之一,并且这些信息都没有回答为什么C#不支持它的问题
答案 3 :(得分:0)
只是发布这个地方谷歌找到它... 我正在调查这个因为我想要一个接口,我可以在其中返回实现特定接口的任意类的集合/枚举。
如果您可以定义要返回的具体类型,则可以相应地定义您的界面。然后它将在编译时检查是否满足约束(任何子类型)。
我提供了一个可能对您有帮助的例子。
正如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