C# 学习逆变和协变
协变和逆变主要作用是为了让泛型接口、委托和数组在类型转换时更加灵活,减少不必要的代码,
C#只允许在接口和委托上使用out和in修饰逆变和协变,并对其行为进行了约束,避免破坏其类型安全,所以协变只允许返回,逆变只允许输入,
我们知道子类可以隐式转为父类,因为子类继承了父类的所有特性,父类有的行为字类必然也有,所以整个过程都是类型安全的,可以直接转换,
但是我们无法直接将List<string>隐式转换为List<object>,因为它们没有继承关系,想要转换的话就就需要先创建一个List<object>然后再把List<string>中的数据一个一个的扔到List<object>中,然而我们都知道它们的泛型类型本质上是有继承关系的,理论上不应该这么别扭,总感觉可以直接转换的样子,这个时候就能用到协变了,
通过协变就能使泛型类型的输出方向使用其派生类型,
先看一下官方默认实现的协变,平常用的时候是不是感觉很方便,
object[] arr = new string[4]; IEnumerable<object> values = new string[4]; //List<object> list = new List<string>(); //报错,因为没有实现协变
我们也可以自己简单实现一个具有协变特性的集合
public void Test() { MyList<string> list = new MyList<string>(2); list.Add("aaa"); list.Add("bbb"); IMyList<object> objectList = list; //不报错了,因为IMyList<out T>实现了协变 // 通过协变接口访问元素 Console.WriteLine(objectList[0]); Console.WriteLine(objectList[1]); } /// <summary> /// 使用 out 关键字标记 T 为协变类型参数 /// </summary> public interface IMyList<out T> { T this[int index] { get; } // 只能作为输出(返回值) int Count { get; } } public class MyList<T> : IMyList<T> { readonly T[] _items; public T this[int index] => _items[index]; public int Count { get; private set; } public MyList(int capacity) { _items = new T[capacity]; } public void Add(T item) { _items[Count++] = item; } }
逆变正好是反过来的,它允许泛型类型的输入方向使用基类型,这种行为也是安全的,因为基类型可以接受派生类型的所有输入,
C#的Action委托实现了逆变,它可以将父类泛型委托隐式转换为子类泛型委托,然后传递参数的时候使用子类型,
void Output(object obj) { Console.WriteLine(obj.ToString()); } Action<string> stringAction = Output; //利用逆变实现类型转换 stringAction("测试啊123");
也可以自己实现一个逆变委托,
/// <summary> /// 使用in修饰委托让其具有逆变特性 /// </summary> delegate void Output<in T>(T val); public void Test() { Output<object> output = p => Console.WriteLine(p.ToString()); Output<string> outputString = output; //可以直接将父类泛型委托转换为子类泛型委托 outputString("测试啊123abc"); }

浙公网安备 33010602011771号