Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
439 views
in Technique[技术] by (71.8m points)

generics - Why does the C# compiler complain that "types may unify" when they derive from different base classes?

My current non-compiling code is similar to this:

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) { }
}

The C# compiler refuses to compile this, citing the following rule/error:

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

I understand what this error means; if TA could be anything at all then it could technically also be a B which would introduce ambiguity over the two different Handle implementations.

But TA can't be anything. Based on the type hierarchy, TA can't be a B - at least, I don't think it can. TA must derive from A, which does not derive from B, and obviously there's no multiple class inheritance in C#/.NET.

If I remove the generic parameter and replace TA with C, or even A, it compiles.

So why do I get this error? Is it a bug in or general un-intelligence of the compiler, or is there something else I'm missing?

Is there any workaround or am I just going to have to re-implement the MyFoo generic class as a separate non-generic class for every single possible TA derived type?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

This is a consequence of section 13.4.2 of the C# 4 specification, which states:

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.

Note that second sentence there.

It is therefore not a bug in the compiler; the compiler is correct. One might argue that it is a flaw in the language specification.

Generally speaking, constraints are ignored in almost every situation in which a fact must be deduced about a generic type. Constraints are mostly used to determine the effective base class of a generic type parameter, and little else.

Unfortunately, that sometimes leads to situations where the language is unnecessarily strict, as you have discovered.


It is in general a bad code smell to implement "the same" interface twice, in some way distinguished only by generic type arguments. It is bizarre, for example, to have class C : IEnumerable<Turtle>, IEnumerable<Giraffe> -- what is C that it is both a sequence of turtles, and a sequence of giraffes, at the same time? Can you describe the actual thing you're trying to do here? There might be a better pattern to solve the real problem.


If in fact your interface is exactly as you describe:

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

Then multiple inheritance of the interface presents another problem. You might reasonably decide to make this interface contravariant:

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

Now suppose you have

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

And

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

And now things get really crazy...

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

Which implementation of Handle gets called???

See this article and the comments for more thoughts on this issue:

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...