2

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.

Veruthas
  • 39
  • 4

3 Answers3

0

Just modifyTracer<in T> to Tracer (non-generic) and define void Perform(T t, Tracer tracer);. Your code was not using T in Tracer anyways.

Since you edited your question with new details, the alternative fix is to remove in from generics definition. You don't need it. Another way to achieve what you want is following:

public interface Performer<T>
{
    bool Perform(T t, Tracer tracer = null);
}

public interface Tracer
{
    bool Notify<T>(Performer<T> performer);
}

Note: drop ref bool and return bool instead

Sherlock
  • 1,022
  • 8
  • 19
  • If I were to change the method to `void Notify(T t, Performer performer)` What would the solution be? – Veruthas Jun 04 '18 at 18:57
  • ````public interface Perfomer { void Perform(T t, Tracer tracer); } public interface Tracer { void Notify(); }```` – Sherlock Jun 04 '18 at 18:59
0

From what you've provided I'd avoid the issue all together, Modify the Tracer interface to remove the T because it's not needed:

public interface INotify
{
    void Notify();
}

Then just take in an the new interface in your performer

public interface Perfomer<in T>
{
    void Perform(T t, INotify entity);
}

PS: there might be a type in your interface name Perfomer => Performer

johnny 5
  • 19,893
  • 50
  • 121
  • 195
0

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();
}
John Wu
  • 50,556
  • 8
  • 44
  • 80