具有两个协变接口的C#协方差问题

时间:2018-06-04 18:36:10

标签: c# .net generics covariance contravariance

我有两个协变的接口,两个接口都是这样传递的:

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

public interface Tracer<in T>
{
    void Notify();
}

然而即使两个接口都标记为协变,并且T只被用作输入,我仍然会收到错误:

"Invalid variance: The type parameter 'T' must be covariantly valid on
'Perfomer<T>.Do(T, Tracer<T>)'. 'T' is contravariant. [_Console].

为什么使用相同类型的协变接口参数的任何想法都会导致T逆变?

修改  (对不起,我是StackOverflow的新手,基于我的回答,我知道在我的问题中我应该更加准确,我只是试图消除尽可能多的噪音到单个错误。)

代码实际上有两个接口,通常有类似的接口:

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);
}

它的目的是允许可选的“跟踪器”看到事情发生/修改表演者的结果。

3 个答案:

答案 0 :(得分:0)

只需将Tracer<in T>修改为Tracer(非通用)并定义void Perform(T t, Tracer tracer);即可。 无论如何,您的代码未在T中使用Tracer

由于您使用新的详细信息编辑了问题,因此替代修复方法是从泛型定义中删除。你不需要它。另一种实现目标的方法是:

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

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

注意:删除ref bool并返回bool而不是

答案 1 :(得分:0)

根据您提供的内容,我一起避免了这个问题,修改Tracer界面以删除T,因为它不需要:

public interface INotify
{
    void Notify();
}

然后只需在表演者中接受新界面

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

PS:您的界面名称中可能有一个类型Perfomer =&gt;表演者

答案 2 :(得分:0)

当您声明Performer是逆变的时,您声明Performer对T做的任何事情也可以对T的更具体版本执行。例如,作用于对象的动作可以是给定一个字符串,它就好像该字符串是一个对象。

例如,您可以这样做,因为所有流都支持Length

class MyClass : Performer<Stream>
{
    void Perform(Stream t)
    {
        Console.WriteLine(t.Length)
    }
}
Performer<FileStream> p = new MyClass();
p.Perform(new FileStream());

但你不能这样做,因为你给了它一个不支持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

到目前为止一切顺利。现在让我们添加第二个参数:

class MyClass : Performer<Stream>
{
    void Perform(Stream t, Tracer<Stream> tracer)
    {
        Console.WriteLine(tracer.Notify())
    }
}

为了实现这一点,逆变必须起作用。如果逆变有效,则意味着Perform可以将Tracer<FileStream>(您传入的)存储在键入为Tracer<Stream>的变量中(这是如何实现的)。这意味着Tracer在类型参数方面必须 协变

因此,您可以通过将in更改为out来修复代码,如下所示:

public interface Performer<in T> 
{
    void Perform(T t, Tracer<T> tracer);  
}

public interface Tracer<out T> //out instead of in
{
    void Notify();
}