2009-08-09 50 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