Interface implemented twice "types may unify"; why does this workaround work?
c#, inheritance, interface-implementation
Solution
Consider this implementation:
public class MyClass<T1, T2> : IMyInterface<T1, T2>, IMyInterface<T2, T1>
{
/* implementation for IMyInterface<T1, T2> here. */
/* implementation for IMyInterface<T2, T1> here. */
}
What does `MyClass<int, int>` implement? It implements `IMyInterface<int, int>` twice, because `IMyInterface<T1, T2>` and `IMyInterface<T2, T1>` unify when `T1` and `T2` are equal. That's why implementing both `IMyInterface<T1, T2>` and `IMyInterface<T2, T1>` on the same class is disallowed. The same reasoning would apply if you tried to implement, for example, `IMyInterface<int, T1>` and `IMyInterface<T2, double>`: the type expressions unify for `T1 = double, T2 = int`.
Consider this implementation:
public class MyClass<T1, T2> : MyClassBase<T1, T2>, IMyInterface<T1, T2>
{
/* implementation for IMyInterface<T1, T2> here. */
}
public class MyClassBase<T1, T2> : IMyInterface<T2, T1>
{
/* implementation for IMyInterface<T2, T1> here. */
}
What you've done is place a priority on `IMyInterface<T1, T2>` over `IMyInterface<T2, T1>`. In the event that `T1` and `T2` are equal and you have an instance of `MyClass<T1, T2>`, the `IMyInterface<T1, T2>` implementation will be selected. If you have an instance of `MyBaseClass<T1, T2>`, the `IMyInterface<T2, T1>` implementation will be selected.
Here's a toy program that shows you the behaviors. In particular notice the behavior of `a_as_i.M(0, 1)` and `a_as_b.M(0, 1)`. If you were to implement `I<T2, T1>` explicitly on `B<T1, T2>` (by prefixing the method name with `I<T2, T1>.`), it would be impossible to call it using compile-time syntax. Reflection would be necessary.
interface I<T1, T2>
{
void M(T1 x, T2 y);
}
class A<T1, T2> : B<T1, T2>, I<T1, T2>
{
public void M(T1 x, T2 y)
{
Console.WriteLine("A: M({0}, {1})", x, y);
}
}
class B<T1, T2> : I<T2, T1>
{
public void M(T2 x, T1 y)
{
Console.WriteLine("B: M({0}, {1})", x, y);
}
}
class Program
{
static void Main(string[] args)
{
//Outputs "A: M(0, 1)"
var a = new A<int, int>();
a.M(0, 1);
//Outputs "B: M(0, 1)"
var b = new B<int, int>();
b.M(0, 1);
//Outputs "A: M(0, 1)" because I<T1, T2>
//takes precedence over I<T2, T1>
var a_as_i = a as I<int, int>;
a_as_i.M(0, 1);
//Outputs "B: M(0, 1)" despite being called on an instance of A
var a_as_b = a as B<int, int>;
a_as_b.M(0, 1);
Console.ReadLine();
}
}
Problem
I've run into a compiler error when attempting to implement an interface twice for the same class like so: ``` public class Mapper<T1, T2> : IMapper<T1, T2>, IMapper<T2, T1> { /* implementation for IMapper<T1, T2> here. */ /* implementation for IMapper<T2, T1> here. */ } ``` The error: 'Mapper' cannot implement both 'IMapper' and 'IMapper' because they may unify for some type parameter substitutions. Why does this workaround work? I'm wondering if I've solved the problem or just tricked the compiler. ``` public class Mapper<T1, T2> : MapperBase<T1, T2>, IMapper<T1, T2> { /* implementation for IMapper<T1, T2> here. */ } public class MapperBase<T1, T2> : IMapper<T2, T1> { /* implementation for IMapper<T2, T1> here. */ } ``` EDIT: I've updated `MyClass`, `MyClassBase`, and `IMyInterface` to `Mapper`, `MapperBase`, and `IMapper` to represent a more real-world scenario where this issue may present itself.