重写泛型方法的约束

时间:2016-07-25 10:35:48

标签: c# generics

我试图理解约束(类或泛型方法)如何影响方法本身。以此代码为例:

class Base<T> where T:class, IComparable<T>
{
    public virtual void Method(T obj) { }
}

class Derived<T> : Base<T> where T:class, IComparable<T>, IEnumerable<T>
{
    public override void Method(T obj) { }
}

此代码编译良好,编译器/运行时能够解析对非泛型方法的多态调用&#39;方法&#39;它采用通用类型的参数。在基类和派生类中,类型参数的约束是不同的。

我还指定了一个类约束来阻止可能导致问题的值类型,因为为每个值类型实例化生成了一个不同的类,而只为引用类型实例化了一个这样的类。

另一方面,以下代码无法编译。

class Base
{
    public virtual void Method<T>() where T : class, IComparable<T> { }
}

class Derived : Base
{
    public override void Method<T>() where T : class, IComparable<T>, IEnumerable<T> { }
}

C#的语言规范表明对泛型方法的约束与最重要的方法一样,并且指定任何约束都是非法的。我在谷歌上做了一些搜索,发现由于支持多态调用(关于维护方法表等)的复杂性,这是不允许的。但我仍然不明白为什么它在上面的案例1中有效,除了它是通用的类之外​​。编译器/运行时如何能够在案例1中执行此操作,而案例2被标记为编译器错误?

2 个答案:

答案 0 :(得分:4)

在情况1中,当T由类确定时,该方法可用于任何类型为T的对象。对于任何特定的类,只有一种类型T,所以重写很简单。您可以使用更广泛的类型T来生成Base&lt; T&gt;的事实。而不是生成Derived&lt; T&gt;没问题。

在案例2中,可以针对任意数量的类型调用方法。但是,Derived类中允许的类型只是Base类中允许的类型的子集,这意味着你有一个&#34;部分覆盖&#34;,这会让事情变得一团糟。

答案 1 :(得分:3)

我们只考虑问题&#34;为什么在虚拟覆盖中向通用方法添加约束是非法的?&#34;因为那真的很简单。

class Foo : IComparable<Foo> { ... }
...
Base b = new Derived();
b.Method<Foo>();

首先,这应该是非法的吗?是。调用b.Method<Foo>实际上会调用Derived.Method<Foo>,但Foo不符合约束。所以这必须是非法的。

应该在哪一行代码中报告错误? Foo的声明是合法的。将Derived转换为Base是合法的。从编译器的角度来看,对b.Method<Foo>的调用是合法的; b类型为BaseFoo符合Base.Method的约束。因此,无法在任何这些行上报告错误。报告错误的唯一位置是Base.Method中具有导致问题的where子句的行。因此,这种where条款必须是非法的,以防止任何人编写上述其他合法的程序片段。

那么你的第一个案例,那个类是通用的呢?那么,在那种情况下你会如何得到Base<Foo>?当然不是来自Derived<Foo>的实例,因为你甚至不能在第一时间制作这种类型!等效问题程序是:

Base<Foo> b = new Derived<Foo>();
b.Method();

现在应该在哪里报告错误?显然,它可以在创建Derived<Foo>的行上报告!因此,没有必要使附加的where条款非法。