C#泛型协变与逆变

C#2.0开始引入协变和逆变,4.0开始引入泛型协变和逆变.

大家都知道子类和父类是可以直接相互转换的,但是泛型是不行的,因为涉及的类型安全问题:

List<string> strs = new List<string>();
 List<object> objs = strs;

上面的代码报错,List<string>转换成List<object>失败.那是因为List<T>的类型参数不支持协变.

我们把上面的代码稍微改一下,如下:

List<string> strs = new List<string>();
IEnumerable<object> objs = strs;

不报错,List<string>转换成List<object>成功,那是因为IEnumerable<T>的类型参数支持协变.

这就引入了泛型协变和泛型逆变概念,便于在一定条件下,可以进行转换,,,,,,

如下demo展示了协变和逆变的具体用法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 泛型的协变和逆变
{
    class Program
    {
        //自定义一个支持逆变的泛型委托
        public delegate void D_in<in T>(T t);
        //自定义一个支持协变的泛型委托
        public delegate T Del_out<out T>();

        //static void M(object o) { }
        static void Main(string[] args)
        {
            //1.变体包括协变和逆变,通过下面下面两句话,就能理解什么是协变和逆变了.
            //协变:父类引用指向子类对象,out关键字
            //public interface IEnumerable<out T> : IEnumerable
            IEnumerable<object> list = new List<string>();

            //逆变:子类引用指向父类对象,in关键字
            //public delegate void Action<in T>(T obj);
            Action<object> action_obj = o => {/*委托引用的方法的执行代码*/ };
            Action<string> myAction = action_obj;

            //2.数组的协变
            //协变的类型只能是引用类型,不能是值类型
            //如下,字符串数组支持协变
            object[] objects = new string[10];
            
            //但int[]不支持协变,因为int是值类型,如下语句报错不兼容.
            //object[] objects = new int[10];

            //如下数组变量类型为delegate,而object是一切类型的父类,如下语句协变成功.
            Delegate[] delegates_del = new Action<int>[10];
            object[] delegates_obj = new Action<int>[10];

            //3.泛型的协变和逆变
            //关键字:泛型,类型参数,out,in
            //泛型委托和泛型接口支持协变和逆变
            //在泛型委托中,协变只能用于返回类型,而逆变只能用于输入参数,如下泛型委托类型,T为输入参数,TResult为返回参数.
            //public delegate void Action<in T>(T obj);
            //public delegate TResult Func<in T, out TResult>(T arg);
            
            //4.自定义Base,Derived类型,Derived类派生自Base类,在自定义Func<Derived, Base>委托中实现协变和逆变.
            //自定义两个委托类型Del_out<out T>、D_in<in T>,分别支持协变/逆变,体现其自动转换
            D_in<Base> d_base = M_in;
            D_in<Derived> d_in_derived = d_base;
            //或者写成一句代码也可以
            D_in<Derived> d_in_derived1 = d_base;

            //调用委托
            d_in_derived(new Derived());
            d_in_derived1(new Derived());
            
            Del_out<Derived> d_out_derived = M_out;
            Del_out<Base> d_out_base = d_out_derived;
            //或者写成一句代码也可以
            Del_out<Base> d_out_base1 = M_out;
            
            //调用委托
            Base bb = d_out_base1();

            //5.//声明一个Func泛型委托Func<Derived, Base>,的输入参数类型是in Derived,返回参数类型out Base,体现协变和逆变的自动转换
            Func<Derived, Base> myFunc = MyMethod;
            //调用委托
            Base result = myFunc(new Derived());
            //上面的写法似乎有点绕,换成下面的写法就清晰了.
            Func<Base, Derived> MyFunc_original = MyMethod;
            Func<Derived, Base> myFunc_AutoChange = MyFunc_original;
            //调用委托
            Base res = myFunc_AutoChange(new Derived());
        }
        static Derived MyMethod(Base b) => b as Derived ?? new Derived();
        static void M_in(Base b) { }
        static Derived M_out() { return new Derived(); }
    }
    public class Base{}
    public class Derived :Base{ }
}

 .NET Framework 4 引入了对多个现有泛型接口的变体支持。 变体支持允许实现这些接口的类进行隐式转换,以下说明来源于微软的说明文档:

自 .NET Framework 4 起,以下接口为变体:

自 .NET Framework 4.5 起,以下接口是变体:

IReadOnlyCollection<T>

demo里少了泛型接口的协变和逆变实例,以后补充吧~~~

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值