Co/contravariant接口和可分配性

car*_*ett 2 c# covariance contravariance

我有一些问题,我认为,差异,我不完全理解.我有一个带有两个类型参数的通用接口,如下所示:

public interface IInvoker<TParameter, TResult> {
    TResult Invoke(TParameter parameter);
}
Run Code Online (Sandbox Code Playgroud)

现在,在我的情况,我想我们TATB抽象类,是这样的:

public abstract class AbstractParameter {
    public int A { get; set; }
}
public abstract class AbstractResult {
    public string X { get; set; }
}

public class Parameter1 : AbstractParameter {
    public int B { get; set; }
}
public class Result1 : AbstractResult {
    public string Y { get; set; }
}
// ... Many more types
Run Code Online (Sandbox Code Playgroud)

然后我想处理一组不同的实现IInvoker<,>,所以我想我可以做这样的事情

public class InvokerOne : IInvoker<Parameter1, Result1> { /* ... */ }
public class InvokerTwo : IInvoker<Parameter2, Result2> { /* ... */ }

// ..
IInvoker<AbstractParameter, AbstractResult>[] invokers = { new InvokerOne(), new InvokerTwo() };
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为据我所知,IInvoker<AbstractParameter, AbstractResult>无法从IInvoker<Parameter1, Result1>(和朋友)分配.首先,我认为这是拍打一些inout我的界面(interface IInvoker<in TParameter, out TResult>)的时间,但这没有帮助.

但我不明白为什么?据我所知,任何人都IInvoker<AbstractParameter, AbstractResult>应该可以打电话Invoke,对吧?我错过了什么?

Lee*_*Lee 5

问题是TResult类型参数是反式变量,但是你试图在你的任务中共同使用它们,例如

IInvoker<AbstractParameter, AbstractResult> i1 = new InvokerOne();
Run Code Online (Sandbox Code Playgroud)

TResult是共同变体,因此可以AbstractResult成为一个更大的类型Result1.但是,由于TParameter是反型变体,因此TParameter必须是比较小的类型Parameter1,但事实并非如此AbstractParameter.

如果以上内容有效,您可以:

class OtherParameter : AbstractParameter { ... };
IInvoker<AbstractParameter, AbstractResult> i1 = new InvokerOne();
i1.Invoke(new OtherParameter());
Run Code Online (Sandbox Code Playgroud)

这不安全.

但是你可以拥有以下内容:

public class OtherParameter1 : Parameter1 { }
IInvoker<OtherParameter1, AbstractResult> i1 = new InvokerOne();
Run Code Online (Sandbox Code Playgroud)

这里OtherParameter1可以作为参数传递,Invoke因为它始终是有效的Parameter1.