C# 泛型(二)

通过前一篇(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();
            }
View Code

协变的出现很好的解决了这种情况

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();
    }
}
View Code
           {
                IEnumerable<Cat> cats = new List<Cat>();
                IEnumerable<Animal> animals = new List<Animal>();
            }
            {
                IEnumerable<Animal> animals = new List<Cat>();
            }
View Code

学完了,协变,在看看逆变

   public interface ICustomerList<in InT>
    {
        void Show(InT inT);
    }
    public class CustomerList<InT> : ICustomerList<InT>
    {
        public void Show(InT inT)
        {
            return;
        }
    }
View Code

动物会叫,猫一定也会叫

           {
                ICustomerList<Cat> cats = new CustomerList<Cat>();
                ICustomerList<Animal> animals = new CustomerList<Animal>();
            }
            {
                ICustomerList<Cat> animals = new CustomerList<Animal>();
                animals.Show(new Cat());
            }
View Code

协变(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;
        }
    }
View Code
           {
                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>();//协变+逆变
            }
View Code

 

六、泛型缓存

我们知道静态字段存放在特定的静态区域,读写都很快,而泛型缓存正是运用的这种特性

    /// <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)
    }
View Code

泛型类,会为每个不同类型生成一份副本,效率很高

在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());
                }
View Code

执行结果如下:

静态构造函数被执行了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;

posted @ 2019-03-09 22:02  德乌姆列特  阅读(229)  评论(0编辑  收藏  举报