我想知道为什么C#团队决定不支持非泛型的共同/逆转,因为它们可能同样安全。这个问题相当主观,因为我不希望团队成员做出回应,但有人可能会有我(和Barbara Liskov)缺乏的洞察力。
让我们看看这个示例界面:
public interface ITest
{
object Property
{
get;
}
}
以下实现将失败,尽管完全安全(我们总是可以返回更具体的类型而不违反接口 - 不是在C#中,但至少在理论上)。
public class Test : ITest
{
public string Property
{
get;
}
}
如果接口包含一个setter,代码自然不安全,但这并不是限制整体实现的理由,因为可以通过使用out / in来声明安全性,就像泛型一样。
答案 0 :(得分:2)
CLR不支持协变返回类型,而从.NET 2.0开始支持委托/接口泛型差异。
换句话说,它不是由C#团队决定,而是CLR团队。
至于为什么CLR不支持正常方差 - 我不确定,除了增加复杂性之外,大概没有必要的感知效益。
编辑:要反驳关于返回类型协方差的观点,请参阅CLI规范的第8.10.4节,讨论vtable“slots”:
对于标记的每个新成员 “期待现有的插槽”,看看是否 完全匹配的种类(即田地或 方法),名称和签名存在 并且如果找到则使用该插槽, 否则分配一个新的插槽。
从第II节第9.9节开始:
具体来说,为了确定 会员是否隐藏(静态或 实例成员)或覆盖(for 虚拟方法)来自基础的成员 类或接口,只需替换 每个通用参数及其 泛型参数,并比较 结果会员签名。
没有迹象表明比较是以允许变化的方式完成的。
如果您认为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
使返回类型为“string”而不是“object”:
.method public hidebysig virtual instance string Foo() cil managed
重建:
ilasm /OUTPUT:Test.exe Test.il
重新运行,输出:
Base.Foo
换句话说,就CLR而言,Derived.Foo不再覆盖Base.Foo。
答案 1 :(得分:1)
考虑使用允许动态方法解析的示例,仅使用不同的返回类型(绝对错误)
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??
答案 2 :(得分:1)
CLR不支持方法覆盖的差异,但是接口实现有一种解决方法:
public class Test : ITest
{
public string Property
{
get;
}
object ITest.Property
{
get
{
return Property;
}
}
}
这将实现与协变覆盖相同的效果,但只能用于接口和直接实现