zoukankan      html  css  js  c++  java
  • C# 泛型

    1. 泛型的意义

    类型安全(更好的编译时检查),性能增强(避免装箱和拆箱),代码复用(减少了功能相似的代码)
    

    2. 种类:

    • 泛型类型(包括类,结构,接口,委托,数组)
    • 泛型方法。

      枚举、属性、构造函数、字段 这些没有对应的泛型

    3. 约束 :

    • where T: class (必须是引用类型)
    • where T:struct (必须是值类型,nullable 类型的值类型除外)
    • where T: 基类名 (限制类型参数必须是继承自指定的基类,或是基类本身)
    • new() (必须有无参构造函数,只能用于引用类型(如果类型是结构类型也不会报错),并且必须放在约束的最后如 class Sample<T> where T : class, IDisposable, new() )
    • where T:接口名 (限制类型参数必须是实现指定接口)
    • where T:U (为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束.)
    • 组合约束

      上面的几种约束可以形成组合约束。若一个类型参数有多个约束,约束之间用 , 分开; 如果包含多个类型参数 ,多个类型参数用 where 分开。如

      public class KeyValue<TKey,TValue> where TKey : new() where TValue: Base,new()

    4. 相关术语:

    • 形参(一般用 paramter 描述)
    • 实参(一般用 argument 描述)
    • 类型参数:是一种形参,如 List<T> 中的 T
    • 类型实参 ,如 List<int> 中的 int
    • 泛型的 开放类型
      指的是带有 类型参数(且未分配类型实参) 的类型,如 Type myType= typeof(List<T>) ;中的变量 myType;
    • 泛型的 封闭类型; 已经分配具体 类型实参 的类型,如 Type myType2= typeof(List<int>) ; 中的变量 myType2;

    5. 泛型方法

    • 带有类型参数的方法不一定都是泛型方法

      public class Animal<T>
          {
              public void Move1(T speed) { }
      
              public void Move2<S>(S speedT) { }
          }
      

      Move2 是泛型方法 , Move1 则不是. 判断一个方法是否是泛型方法,可以通过反射获取到 MethodInfo 对象,然后调用 对象的 IsGenericMethod 方法.

    • 方法重载: 调用方法时,如果有多个方法签名相匹配,则优先调用类型参数较少的方法

    • 类型参数的省略:调用泛型方法时 ,如果是下面的2种情况则可以省略类型参数

      • 使用类型明确的值,传给被调用的方法时 如
          Move<int>(100); // 可以简写成  Move(100);
      
      • 使用泛型方法调用另一个具有相同方法签名的泛型方法时
          public void Move<T>(T speed){}
      
          public void MoveWrapper<T>(T s)
          {
              Move<T>(s);// 可以简写成  Move(s);
          }
      

    6. 协变和逆变:

    • 协变和逆变指的是泛型的类型实参之间的类型转换 。且协变和逆变都是类型安全的.

      C# 只对 接口委托 提供可变性支持(包括协变和逆变),另外支持 数组 的协变(但是仅仅是编译时不报错,允许时访问到内部的具体元素还是会出现运行时异常,这样设计的原因据说是为了兼容 Java)。 类型参数前加 inout ,这种写法是 C# 4.0 才引人的, 这也表明之前的 C#版本不支持协变和逆变

    • 协变(Covariance 允许使用比原始指定的类型派生程度更大的类型)

      通用的表示是 Ixxx<父类或父接口> iBase = new xxx<子类或子接口>(); ,

      其中 Ixxx接口中必然有 类型参数 前面带有 out 关键字修饰
      如: IEnumerable<Animal> animals= new List<Cat>();
      当泛型的类型参数只描述返回类型参数(输出)的操作时,协变是类型安全的;
      若类型参数前加 out ,表明该类型只能作为函数的返回类型使用,不能作为输入

    • 逆变(Contravariance 允许使用比原始指定的类型派生程度更小的类型)

      通用的表示是 Ixxx<子类或子接口> iBase = new xxx<父类或父接口>();
      其中 Ixxx接口中必然有 类型参数 前面带有 in 关键字修饰。
      如:

          Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };
          Action<Derived> d = b;
          d(new Derived());
      

      当类型参数只描述接受类型参数(输入)的操作时,逆变就是安全的 。
      若类型参数前加 in ,表明该类型只能作为函数的输入类型使用,不能作为输出类型。

    • 不变体(Invariance 只能使用原始指定的类型;泛型类型参数既不是协变类型,也不是逆变类型) 如: List<Cat> cats= new List<Cat>();

    7. 进阶内容

    • 泛型类型内的静态字段:

      不同的封闭类型具有不同的静态字段

    • JIT 编译器如何处理泛型
      1. JIT 为每个以值类型作为类型实参的 封闭类型 都创建不同的代码
      2. 所有使用引用类型作为类型实参的 封闭类型 都共享相同的本机代码
        这是因为所有引用类型的变量实际上只是指向堆上对象的指针.而所有的指针都可以使用相同的方式操作.
  • 相关阅读:
    虚拟机CentOS 7 网络连接显示"线缆被拔出"
    sqlplus下删除退格,出现^H^H
    “服务器发送了一个意外的数据包。received:3,expected:20“问题的解决方法
    Oracle 12c创建PDB用户并设置默认表空间
    今日进度
    今日进度
    给王老师的建议
    今日进度
    今日进度
    每周总结
  • 原文地址:https://www.cnblogs.com/francisXu/p/13667540.html
Copyright © 2011-2022 走看看