Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics in C# with multiple generic types leads to allowed and disallowed ambiguity

I recently wrote this and was surprised that it compiles:

public class MyGeneric<U, V> {
  MyGeneric(U u) { ... }
  MyGeneric(V v) { ... }
  public void Add(U u, V v) { ... }
  public void Add(V v, U u) { ... }
}

If I use this class as follows, I get an "Ambiguous constructor reference" and an "Ambiguous invocation" if I call Add.

var myVar = new MyGeneric<int, int>(new MyIntComparer());

Obviously, there's no ambiguity when I use int and double as generic types, except of course when I use both ints, which would also both assign to a double.

var myVar = new MyGeneric<int, double>(new MyIntComparer());
myVar.Add(3, 5);

So then I thought that the following was also allowed, but surprisingly I got an error. Why is the following not allowed to compile?

public interface IMyInterface<T, S> {
  void Add(T t, S s);
}

public class MyGeneric<U, V> : IMyInterface<U, V>, IMyInterface<V, U> {
  public MyGeneric(U u) { }
  public MyGeneric(V v) { }
  void IMyInterface<U, V>.Add(U u, V v) { ... }
  void IMyInterface<V, U>.Add(V v, U u) { ... }
}

Regardless if I use implicit or explicit interface implementation, the compiler states that

'MyGeneric<U,V>' cannot implement both 'IMyInterface<U,V>' and 'IMyInterface<V,U>' because they may unify for some type parameter substitutions

And why is the first allowed to write?

like image 672
Andreas Avatar asked Dec 05 '12 08:12

Andreas


2 Answers

1- Why is the following not allowed to compile ?

Part of the response is in that post : Why does the C# compiler complain that "types may unify" when they derive from different base classes?

The section 13.4.2 of the C# 4 specification states:

The interfaces implemented by a generic type declaration must remain unique for all possible constructed types. Without this rule, it would be impossible to determine the correct method to call for certain constructed types.

2- And why is the first allowed to write?

The compiler perform the generic type check at compile time, the section 7.4.3.5 of the C# 4 specification states:

While signatures as declared must be unique, it is possible that substitution of type arguments results in identical signatures. In such cases, the tie-breaking rules of overload resolution above will pick the most specific member. The following examples show overloads that are valid and invalid according to this rule:

interface I1<T> {...}
interface I2<T> {...}
class G1<U>
{
    int F1(U u);                    // Overload resulotion for G<int>.F1
    int F1(int i);                  // will pick non-generic
    void F2(I1<U> a);               // Valid overload
    void F2(I2<U> a);
}
class G2<U,V>
{
    void F3(U u, V v);          // Valid, but overload resolution for
    void F3(V v, U u);          // G2<int,int>.F3 will fail
    void F4(U u, I1<V> v);      // Valid, but overload resolution for   
   void F4(I1<V> v, U u);       // G2<I1<int>,int>.F4 will fail
    void F5(U u1, I1<V> v2);    // Valid overload
    void F5(V v1, U u2);
    void F6(ref U u);               // valid overload
    void F6(out V v);
}
like image 177
AlexH Avatar answered Sep 24 '22 01:09

AlexH


It's part of the language specification, as explained in the accepted answer here :

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

Section 13.4.2 of the C# 4 specification 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.

I guess the difference between your two example is that the second uses interfaces (checked for duplicates, per the language spec) but the first uses types (not checked for duplicates, despite potentially causing ambiguity as you have seen).

like image 26
Graham Griffiths Avatar answered Sep 22 '22 01:09

Graham Griffiths