两个协变接口的 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();
}
我有两个都是协变的接口,它们都像这样相互传递:
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();
}