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



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

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

其目的是允许可选的"跟踪器"看到事情发生/修改执行者的结果。

当您声明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();
}

从您提供的内容来看,我会一起避免这个问题,修改跟踪器接口以删除 T,因为它不需要:

public interface INotify
{
void Notify();
}

然后只需在您的表演者中引入新界面

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

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

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

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

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

注意:删除引用布尔值并返回布尔值

最新更新