两个协变接口的 C# 协变问题

C# covariance issue with two covariant interfaces

我有两个都是协变的接口,它们都像这样相互传递:

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 逆变吗?


编辑 (抱歉,我是 Whosebug 的新手,根据我意识到我的问题应该更准确的答案,我只是试图消除尽可能多的噪音以消除单个错误)。

代码实际上有两个接口,接口大致相似:

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

它的目的是让可选的 "tracer" 可以看到 happen/modify 执行者的结果。

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

由于您使用新的详细信息编辑了问题,替代解决方法是从泛型定义中删除 in。你不需要它。另一种实现你想要的方法如下:

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

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

注意:删除 ref bool 并改为 return bool

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

public interface INotify
{
    void Notify();
}

然后在您的 performer 中使用新界面

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

PS: 你的接口名称中可能有一个类型 Performer => 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());

但是你不能这样做,因为你给它一个 class 不支持 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();
}