I have two interfaces that are both covariant, with both being passed in to each other like so:
public interface Perfomer<in T>
{
void Perform(T t, Tracer<T> tracer);
}
public interface Tracer<in T>
{
void Notify();
}
However even though both interfaces are marked covariant, and T is only ever being used as input, I'm still getting the error:
"Invalid variance: The type parameter 'T' must be covariantly valid on
'Perfomer<T>.Do(T, Tracer<T>)'. 'T' is contravariant. [_Console].
Any ideas why having covariant interface parameter using the same type makes T contravariant?
Edit (Sorry, I am new to StackOverflow, based on the answers I realize I should've been more exact in my question, I had just tried to eliminate as much noise as possible to a single error).
The code actually has two interfaces with generally similar interfaces:
public interface Performer<in T>
{
bool Perform(T t, Tracer<T> tracer = null);
}
public interface Tracer<in T>
{
void Notify(Performer<T> performer, T t, ref bool success);
}
It's purpose is to allow the an optional "tracer" to see things happen/modify the results of a performer.
When you declare that Performer
is contravariant, you are declaring that anything a Performer does to a T can also be done to a more specific version of T. For example, an action that acts on a object can be given a string, and it'll just act as if that string is an object.
So for example you could do this, because all streams support Length
:
class MyClass : Performer<Stream>
{
void Perform(Stream t)
{
Console.WriteLine(t.Length)
}
}
Performer<FileStream> p = new MyClass();
p.Perform(new FileStream());
But you can't do this, because you gave it a class that doesn't support IsAsync
:
class MyClass : Performer<FileStream>
{
void Perform(Stream t)
{
Console.WriteLine(t.IsAsync)
}
}
Performer<Stream> p = new MyClass();
p.Perform(new Stream()); //Stream isn't good enough; it has to be a FileStream, since it needs IsAsync
So far so good. Now let's add in that second parameter:
class MyClass : Performer<Stream>
{
void Perform(Stream t, Tracer<Stream> tracer)
{
Console.WriteLine(tracer.Notify())
}
}
In order for this to work, the contravariance has to work. If the contravariance works, it means that Perform
can store a Tracer<FileStream>
(which you pass in) in a variable that is typed as a Tracer<Stream>
(which is how it is implemented). That means that Tracer must be covariant with respect to its type argument.
So you can fix your code by changing in
to out
, like so:
public interface Performer<in T>
{
void Perform(T t, Tracer<T> tracer);
}
public interface Tracer<out T> //out instead of in
{
void Notify();
}