简体   繁体   中英

c# .net 4.0 Covariant vs Contravariant

I'm trying to get something working and struggling with the below when using Contravariance. My understanding is Covariance is where you can return a derived type from a base type. Contravariance is where you can pass in a derived type from a base type as an argument into a class.

So I have the below interface (contravariant):

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

I then have an abstract class

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

and another concrete class

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

Considering the generic type is only ever used as an input and not a return type, I don't see why I cannot do the below:

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.

I have another example using covariance which is below and works fine.

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

Abstract

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

Concrete

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

Now I can do

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

There's more code to the above examples but I've removed it for ease of reading (hopefully!) :-)

Contravariance in this case means that you need to pass in types that are of the specified type or one that is "more specialised" (=> derive from the base type).

Since your Parent implementation in your first example can only process ParentModel , it is invalid to pass in a BaseModel instance. Trying to do new Parent().Process(new BaseModel()) also would not compile. So it is invalid to cast it to IBase<BaseModel> . (assuming ParentModel is a subclass of BaseModel ).

In this case the contravariance model is easier to reason about by thinking that an IBase<in T> "consumes a T". So an IBase<ParentModel> "consumes ParentModel s". This means it can only be passed values that are instances of ParentModel or can be treated as one (effectively only subclasses).

In your second example you are using <out T> , which is "covariant". This can be described as "it produces instances of T ". So a class that "produces" a ParentModel is automatically a "producer" of BaseModel as well: since ParentModel can be cased to BaseModel , IBase<ParentModel> can be casted to IBase<BaseModel> as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM