C#深入理解协变和逆变

C#深入理解协变和逆变

1、背景

是否会有小伙伴有如下的疑惑??

    class Program
    {
        delegate T TestDelegate<T>();//泛型委托
        static void Main(string[] args)
        {
            TestDelegate<Dog> deDog = Target;
    
            TestDelegate<Animal> deAnimal = deDog;//这里会报错
        }
    
        static Dog Target() { return new Dog(); }
    }
    
    public class Animal { }
    
    public class Dog : Animal { }

上述代码中,TestDelegate deAnimal = deDog;这一句编译器会直接报错。因为尽管Dog是Animal的子类,但是委托TestDelegate没有从TestDelegate派生,相反,两个委托对象是同级的,他们从delegate类型派生,后者又派生自object类型。两者没有相互之间的派生关系,因此赋值兼容性不适用。那么有办法可以让我们那样的写法通过编译正常使用吗?

2、协变

协变英文名为Covariance,在泛型方法的原型里面以out表示,有out关键字修饰的参数可以在声明需要父类参数的泛型的地方使用需要子类参数的泛型实例。

所以可以将上述代码修改为:

    class Program
    {
        delegate T TestDelegate<out T>();//泛型委托
        static void Main(string[] args)
        {
            TestDelegate<Dog> deDog = Target;

            TestDelegate<Animal> deAnimal = deDog;//这里会正常
        }

        static Dog Target() { return new Dog(); }
    }

    public class Animal { }

    public class Dog : Animal { }

这样代码就可以正常编译通过了。因为泛型参数有了out的修饰后,可以在声明需要父类参数的泛型的地方使用需要子类参数的泛型实例。

3、逆变

    class Program
    {
        delegate void TestDelegate<T>(T t);//泛型委托
        static void Main(string[] args)
        {
            TestDelegate<Animal> deAnimal = Target;
            TestDelegate<Dog> deDog = deAnimal;//这里会报错
        }

        static void Target(Animal animal) { }
    }

    public class Animal { }

    public class Dog : Animal { }

上述代码同样编译不会通过。改为下面就会正常:

    class Program
    {
        delegate void TestDelegate<in T>(T t);//泛型委托
        static void Main(string[] args)
        {
            TestDelegate<Animal> deAnimal = Target;
            TestDelegate<Dog> deDog = deAnimal;//这里会正常
        }

        static void Target(Animal animal) { }
    }

    public class Animal { }

    public class Dog : Animal { }

逆变英文名叫Contravariance,在泛型的方法签名中以in表示。和协变相反,逆变是指可以使用基于父类参数的泛型实例在要求子类参数的泛型的场合

4、总结

1、in和out关键字,即协变和逆变只适用于委托和接口。

2、系统已经帮我们定义好了一些协变和逆变。public delegate TResult Func();

public delegate void Action(T obj);public interface IEnumerable : IEnumerable

举例说明:

    class Program
    {
        static void Main(string[] args)
        {
            Func<string> func = () => "1e14er14r";
            Func<object> func1 = func;//协变

            Action<object> action = (o) => Console.WriteLine(o);
            Action<string> action1 = action;//逆变

            IEnumerable<string> vs = new List<string> { "sagser", "agwe", "tfwsrfq" };
            IEnumerable<object> vs1 = vs;//协变
        }
    }
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值