I'm stuck trying to translate some Java code that uses (bounded) wildcard generics to C#. My problem is, Java seems to allow a generic type to be both covariant and contravariant when used with a wildcard. For instance:
Java:
interface IInterf { }
class Impl implements IInterf { }
interface IGeneric1<T extends Impl> {
void method1(IGeneric2<?> val);
void method1WithParam(T val);
}
interface IGeneric2<T extends Impl> {
void method2(IGeneric1<?> val);
}
abstract class Generic<T extends Impl> implements IGeneric1<T>, IGeneric2<T> {
public void method1(IGeneric2<?> val2) {
val2.method2(this);
}
}
...works.
C# equivalent (?)
interface IInterf { }
class Impl : IInterf { }
interface IGeneric1<T> where T:Impl {
//Java was:
//void method1(IGeneric2<?> val2);
void method1(IGeneric2<Impl> val);
void method1WithParam(T to);
}
interface IGeneric2<T>where T:Impl {
void method2(IGeneric1<Impl> val);
}
abstract class Generic<T> : IGeneric1<T>, IGeneric2<T> where T : Impl
{
//Java was:
//public void method1(IGeneric2<?> val2) {
public void method1(IGeneric2<Impl> val2)
{
val2.method2(this); //'this': Argument type 'Generic<T>' is not
//assignable to parameter type 'IGeneric1<Impl>'
}
public abstract void method1WithParam(T to);
public abstract void method2(IGeneric1<Impl> val);
}
...fails to compile - see the error in the comment. Which is to be expected, since IGeneric's generic parameter is not marked 'out' for covariance.
If I change this:
interface IGeneric1<T> where T:Impl {
to this
interface IGeneric1<out T> where T:Impl
the error goes away, but another one appears, for the declaration of the method that takes a generic parameter inside the same interface:
interface IGeneric1<T> where T:Impl {
void method1WithParam(T val); //Parameter must be input-safe.
//Invalid variance: The type parameter 'T' must be
//contravariantly valid on 'IGeneric1<out T>'.
Suggestions?
[Also see the follow-up question for a somewhat harder scenario]
You need to translate the Java wildcard generic methods to C# methods that are generic in their own right. For example, this:
should be translated to
It is necessary to repeat the type constraint for
T
specified byIGeneric1<T>
as the type constraint forU
.The reason for this is that in the Java version there are implicit constraints for the type arguments of the parameters of
method1
andmethod2
: if the parameter must be some kind ofIGeneric1<X>
thenX
must obviously be anImpl
because otherwise it could not possibly implementIGeneric1
for that type.In C# the constraints must be explicit, so you repeat what
IGeneric1<T>
andIGeneric2<T>
require ofT
.So the equivalent code would be: