• .NET中的泛型


     

    泛型的引入

        面向对象的编程很大一个好处就是可以达到代码重用,一个类继承另一个类,只需添加一些新的方法,就可以使用基类和自己的方法,很大程度上重用了代码。

        那么对相似的一组操作是否可以达到这种重用的效果,比如对一个数组排序,我们需要它能针对常用的int,string,DateTime等类型通用,像c++中模板那样,调用时只需指定具体类型便可完成同一个操作过程。CLR和C#语言就提供这么一种机制-泛型(generic)

    对于同一个算法,我们对不同类型进行操作时,我们要调用针对具体类型而定制的方法。有了泛型,我们只要指定一个类型,调用一个方法就好了,无论是引用类型,值类型,还是接口,委托均适用。

    泛型的使用:

    例如:List<T> 类型

    List<int> lis = new List<int>();

    lis.Add(1);

    泛型使用T类型代替类型,在使用时才用实际的类型替代,这种功能需要C#语言和CLR层面配合操作。

    泛型的优势

    类型安全:编译器会帮我们检验类型是否对应,这对于编程来说是件好事

    性能更佳:这也是最重要的一点,一种新技术的产生,无非是由易用性和高效率所决定的

    在泛型没出来之前,我们想做到通用,是不是得把类型转成Object(所有类型都隐式派生自Object,都可以转化为Object类型),可以看看ArrayList等类。这种方式存在一定的缺陷

    1、在转化过程中,编译器都需要做类型安全检查,这个过程是要耗费性能的

    2、最可怕的是会遇到装箱与拆箱(就是值类型和引用类型之间的相互转换),了解这其中原理的人都应该清楚,这个是非常耗费性能和内存的(这个有人也做过测试,拿ArrayList和List<T>做比较,引用类型相差无几,但值类型有数十倍之差)

    代码清晰:编写过程几乎不需要类型转换,代码也就更明确,简洁

    代码爆炸问题

    我们看看,一个泛型参数T,它可以接受不同的类型,它编译后真正运行的时候,肯定做了处理的,比如int类型和string类型的比较方式就不同,还有int16,int32最终cup命令很可能都不一样,具体操作时肯定会对应到其相应的实现方法中。

    那么这么多类型,如果每个最后在运行中都生成一份,岂不是“代码爆炸”了。

    其实,这里是可以优化的,拿CLR来说:

    1、例如List<DateTime>类型,编译器第一次遇到,会编译一份并留着,等第二次遇到时,直接拿来用,减少重复。

    2、针对引用类型(所有的引用类型的变量都指向堆中对象的地址,既然都存放的只是个地址,指向哪个不都一样,可以共享),所有的对象其实都共享了这个方法。

    泛型接口和泛型委托

    有了泛型,泛型接口也是少不了的。比如:FCL中的很多类型都继承自非泛型接口,当泛型继承这些非泛型接口时,会产生装箱拆箱问题,这也是性能需要。

    同样是为了类型安全和避免装箱性能损失,CLR也提供了泛型委托

    逆变和协变

    在泛型接口和泛型委托中,其中参数类型可以允许转变,比如 <string> 转到 <object>,这时,编译器并没有给我们做好决策,默认转换,而是需要我们显示的用in和out声明如何转换才行。

    逆变:in 从基类到派生 如:object到string

    协变:out 从派生类到基类

    这个特性主要是让代码有时能重用,比如针对不同类型写一套代码即可。

    泛型中类型推断

    一般方法可以使用泛型定义,以适应不同类型的操作,例如我们熟知的Swap()

    在调用泛型方法时,可以省略类型,直接调用,编译器帮我们做推断

      

            static void Swap(ref int a, ref int b)

    {

    Console.WriteLine("非泛型");

    int temp = a;

    a = b;

    b = temp;

    }

    static void Swap<T>(ref T a, ref T b)

    {

    Console.WriteLine(typeof(T).ToString() + "-" + a);

    T temp = a;

    a = b;

    b = temp;

    }



    static void Main(string[] args)

    {

    int a = 1, b = 2;

    Swap(ref a, ref b); //调用的是非泛型的Swap,在泛型和非泛型同时匹配时,优先非泛型

    Swap<int>(ref a, ref b); //指定类型时,就会调用泛型方法

    string aa = "aa", bb = "bb";

    Swap(ref aa, ref bb); //只匹配到泛型string方法

    Swap<string>(ref aa, ref bb); //也只匹配到泛型string方法

    }
  • 相关阅读:
    欧拉函数的递推形式
    Hadoop运行startdfs.sh报ERROR: Attempting to operate on hdfs as root错误的解决方法
    Solr+ZooKeeper运行报错
    Linux的查找命令
    Solr报警告java.nio.file.NoSuchFileException: /solr/xxx_shard_replica_xx/../../../../contrib/extraction/lib
    利用脚本批量操作Tomcat集群
    大数据组件的日志的时区问题
    利用脚本批量操作ZooKeeper集群
    Solr报错org.apache.solr.common.SolrException: undefined field text
    Solr/SolrCloud报警告Your ZK connection string ( hosts) is different from the dynamic ensemble config ( hosts)的解决方法
  • 原文地址:https://www.cnblogs.com/xiaonan/p/CSharpGeneric.html
Copyright © 2020-2023  润新知