通过前一篇(https://www.cnblogs.com/Dewumu/p/10498831.html)我们大概了解到了泛型的使用,那么泛型还有哪些使用呢?
五、泛型之协变、裂变
“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。
“逆变”则是指能够使用比原始指定的类型更泛型(派生程度更小)的类型。
所谓协变逆变,都是跟泛型相关,
只能放在接口或者委托的泛型参数前面
out 协变covariant 修饰返回值
in 逆变contravariant 修饰传入参数
这里有两个类型,抽象父类(动物类)子类(猫)
public abstract class Animal { } public class Cat:Animal { }
根据里氏转换,我们知道子类可以赋值给父类,但是为什么List<Animal> animals=new List<Cat>();无法编译通过呢?这是因为C#是强类型语言,List<Cat>并不继承 List<Animal>因此List<Cat>需要把集合中的实体转为Animal才能使等式成立;
{ Cat cat = new Cat(); Animal animal = new Cat(); } { List<Cat> cats = new List<Cat>(); //List<Animal> animals=new List<Cat>(); List<Animal> animals = new List<Cat>().Select(a => (Animal)a).ToList(); }
协变的出现很好的解决了这种情况
using System.Runtime.CompilerServices; namespace System.Collections.Generic { // // 摘要: // 公开枚举数,该枚举数支持在指定类型的集合上进行简单迭代。 // // 类型参数: // T: // 要枚举的对象的类型。此类型参数是协变。即可以使用指定的类型或派生程度更高的类型。有关协变和逆变的更多信息,请参见泛型中的协变和逆变。 [TypeDependencyAttribute("System.SZArrayHelper")] public interface IEnumerable<out T> : IEnumerable { // // 摘要: // 返回一个循环访问集合的枚举器。 // // 返回结果: // 可用于循环访问集合的 System.Collections.Generic.IEnumerator`1。 IEnumerator<T> GetEnumerator(); } }
{ IEnumerable<Cat> cats = new List<Cat>(); IEnumerable<Animal> animals = new List<Animal>(); } { IEnumerable<Animal> animals = new List<Cat>(); }
学完了,协变,在看看逆变
public interface ICustomerList<in InT> { void Show(InT inT); } public class CustomerList<InT> : ICustomerList<InT> { public void Show(InT inT) { return; } }
动物会叫,猫一定也会叫
{ ICustomerList<Cat> cats = new CustomerList<Cat>(); ICustomerList<Animal> animals = new CustomerList<Animal>(); } { ICustomerList<Cat> animals = new CustomerList<Animal>(); animals.Show(new Cat()); }
协变(covariant )out 只能是返回值 逆变(contravariant )in只能是参数
public interface IMyList<in inT, out outT> { void Show(inT _int); outT Get(); outT Post(inT _int); //out 只能是返回值 in只能是参数 //void Show(outT _outt); //inT Get(); } public class MyList<InT, OutT> : IMyList<InT, OutT> { public OutT Get() { return default(OutT); } public OutT Post(InT _int) { return default(OutT); } public void Show(InT _int) { return; } }
{ IMyList<Cat, Animal> list1 = new MyList<Cat, Animal>(); IMyList<Cat, Animal> list2 = new MyList<Cat, Cat>();//协变 IMyList<Cat, Animal> list3 = new MyList<Animal, Animal>();//逆变 IMyList<Cat, Animal> list4 = new MyList<Animal, Cat>();//协变+逆变 }
六、泛型缓存
我们知道静态字段存放在特定的静态区域,读写都很快,而泛型缓存正是运用的这种特性
/// <summary> /// 每个不同的T,都会生成一份不同的副本 /// 适合不同类型,需要缓存一份数据的场景,效率高 /// </summary> /// <typeparam name="T"></typeparam> public class GenericCache<T> { static GenericCache() { Console.WriteLine("This is GenericCache 静态构造函数"); _TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff")); } private static string _TypeTime = ""; public static string GetCache() { return _TypeTime; } //common<int>(1) }
泛型类,会为每个不同类型生成一份副本,效率很高
在Main()函数中调用
for (int i = 0; i < 5; i++) { Console.WriteLine(GenericCache<int>.GetCache()); Console.WriteLine(GenericCache<long>.GetCache()); Console.WriteLine(GenericCache<DateTime>.GetCache()); Console.WriteLine(GenericCache<string>.GetCache()); }
执行结果如下:
静态构造函数被执行了4次,这是因为有4种类型
而循环5次,每次相同类型得到的结果是一样的,不同类型的结果是不同的,这就是充分利用了泛型和静态字段的特点达到的效果。
但是这种缓存方式也是有弊端的,清理、释放不方便,所以请勿滥用!!!
本文参考文档:https://www.cnblogs.com/loverwangshan/p/9871548.html;
https://www.cnblogs.com/qixuejia/p/4383068.html;
https://www.cnblogs.com/CLR010/p/3274310.html;
微软文档地址:https://docs.microsoft.com/zh-cn/dotnet/standard/generics/covariance-and-contravariance;
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/covariance-contravariance/;
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/covariance-contravariance/variance-in-generic-interfaces;