c# .net 4.0 协变与逆变

c# .net 4.0 Covariant vs Contravariant

在使用逆变时,我试图让一些东西工作并与下面的问题作斗争。我的理解是协方差是你可以从基类型 return 派生类型的地方。逆变是您可以将基类型的派生类型作为参数传递给 class.

所以我有以下接口(逆变):

public interface IBase<in T> where T: BaseModel
{
        void Process(T model);
}

然后我有一个摘要class

public abstract class Base<T>: IBase<T> where T: BaseModel
{
    public virtual void Process(T model)
    {
       // throw new System.NotImplementedException();
    }
}

还有一个具体的class

public class Parent: Base<ParentModel>
{
    public override void Process(ParentModel model)
    {
        // throw new System.NotImplementedException();
    }
}

考虑到通用类型仅用作输入而不是 return 类型,我不明白为什么我不能执行以下操作:

IBase<BaseModel> baseContravariant = new  Parent();
// This doesn't compile. I will eventually have a list of IBase<BaseMode> to which I'd like to pass in different parent instances.

下面还有另一个使用协方差的示例,效果很好。

public interface IBase<out T> where T : BaseModel, new()
{
    T ProcessAndGet();
}

摘要

public abstract class Base<T>: IBase<T> where T: BaseModel, new()
{
    public virtual T ProcessAndGet()
    {
        var result = new T() as BaseModel;

        // More shizzle here
        return (T)result;
    }
}

混凝土

public class Parent : Base<ParentModel>
{ 
    public override ParentModel ProcessAndGet()
    {
        var x = base.ProcessAndGet();
        return x;
    }
}

现在我可以做

IBase<BaseModel> baseInstance = new Base<BaseModel>();
IBase<BaseModel> derived = new Parent();
baseInstance = derived;

上面的示例还有更多代码,但为了便于阅读,我已将其删除(希望如此!):-)

这种情况下的逆变意味着您需要传入指定类型或 "more specialised"(=> 从基类型派生)的类型。

由于第一个示例中的 Parent 实现只能处理 ParentModel,因此传入 BaseModel 实例是无效的。尝试做 new Parent().Process(new BaseModel()) 也不会编译。所以强制转换为IBase<BaseModel>是无效的。 (假设 ParentModelBaseModel 的子 class)。

在这种情况下,通过认为 IBase<in T> "consumes a T" 更容易推理出逆变模型。所以 IBase<ParentModel> "consumes ParentModels"。这意味着它只能传递作为 ParentModel 实例的值,或者可以被视为一个值(实际上只有 subclasses)。

在您的第二个示例中,您使用的是 <out T>,即 "covariant"。这可以描述为"it produces instances of T"。因此 class "produces" 和 ParentModel 自动也是 BaseModel 的 "producer":因为 ParentModel 可以大小写为 BaseModel, IBase<ParentModel> 也可以转换为 IBase<BaseModel>