为什么C#编译器抱怨“类型可以统一”,当它们从不同的基类派生时?

我当前的非编译代码类似于:

public abstract class A { }

public class B { }

public class C : A { }

public interface IFoo<T>
{
    void Handle(T item);
}

public class MyFoo<TA> : IFoo<TA>, IFoo<B>
    where TA : A
{
    public void Handle(TA a) { }
    public void Handle(B b) { }
}

C#编译器拒绝编译这个,引用以下规则/错误:

‘MyProject.MyFoo<TA>’ cannot implement both ‘MyProject.IFoo<TA>’ and ‘MyProject.IFoo<MyProject.B>’ because they may unify for some type parameter substitutions

我明白这个错误的意思;如果TA可以是任何东西,则它在技术上也可以是B,其将在两个不同的Handle实现上引入歧义。

但TA不能是什么。基于类型层次结构,TA不能是一个B – 至少,我不认为它可以。 TA必须从A​​派生,它不是从B派生的,显然在C#/ .NET中没有多类继承。

如果我删除通用参数和替换TA与C,甚至A,它编译。

那么为什么会得到这个错误?是编译器的一个错误还是一般的不智能,还是有其他我缺少?

有什么解决方法或者我只是要重新实现MyFoo通用类作为一个单独的非通用类为每一个可能的TA派生类型?

这是C#4规范的第13.4.2节的结果,它规定:

If any possible constructed type created from C would, after type arguments are substituted into L, cause two interfaces in L to be identical, then the declaration of C is invalid. Constraint declarations are not considered when determining all possible constructed types.

注意第二句话。

因此它不是编译器中的错误;编译器是正确的。有人可能会认为这是语言规范中的缺陷。

一般来说,在几乎每个必须推断出一个通用类型的事实的情况下,约束都被忽略。约束主要用于确定通用类型参数的有效基类,而其他一些。

不幸的是,有时会导致语言不必要的严格,正如你已经发现的情况。

通常是一个坏的代码气味来实现“相同”的接口两次,在某种程度上只由通用类型参数区分。这是奇怪的,例如,有类C:IEnumerable< Turtle>,IEnumerable< Giraffe> – 什么是C,它是一系列的海龟和一系列的长颈鹿,同时?你能描述你想在这里做的实际事情吗?可能有一个更好的模式来解决真正的问题。

如果实际上你的界面正如你所描述的:

interface IFoo<T>
{
    void Handle(T t);
}

然后,接口的多重继承呈现另一个问题。你可能合理地决定使这个接口逆向:

interface IFoo<in T>
{
    void Handle(T t);
}

现在假设你有

interface IABC {}
interface IDEF {}
interface IABCDEF : IABC, IDEF {}

class Danger : IFoo<IABC>, IFoo<IDEF>
{
    void IFoo<IABC>.Handle(IABC x) {}
    void IFoo<IDEF>.Handle(IDEF x) {}
}

现在事情变得疯狂…

IFoo<IABCDEF> crazy = new Danger();
crazy.Handle(null);

Handle的哪个实现被调用?

查看这篇文章和关于这个问题的更多想法的意见:

http://blogs.msdn.com/b/ericlippert/archive/2007/11/09/covariance-and-contravariance-in-c-part-ten-dealing-with-ambiguity.aspx

http://stackoverflow.com/questions/7664790/why-does-the-c-sharp-compiler-complain-that-types-may-unify-when-they-derive-f

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:为什么C#编译器抱怨“类型可以统一”,当它们从不同的基类派生时?