C# 泛型约束

1、五种约束类型
 (1)可以使用“基类约束”(base class constraint)来指定某个基类必须出现在类型实参中。这种约束是通过指定基类名称来实现的。
 (2)可以使用“接口约束”(interface constraint)来指定某个类型实参必须实现一个或多个接口。这种约束是通过指定接口名称来实现的。
 (3)可以要求类型实参必须提供一个无参数的构造函数,这被称为“构造函数约束”(constructor constraint)。它是通过new()指定的。
 (4)可以通过关键字class指定“引用类型约束”(reference type constraint)来限制某个类型实参必须是引用类型。
 (5)可以通过关键字struct指定“值类型约束”(vlaue type constraint)来限制某个类型实参必须是值类型。

1.1、基类约束
  使用基类约束,我们可以指定某个类型实参必须继承的基类。
   基类约束有两个功能:
    (1)它允许在泛型类中使用由约束指定的基类所定义的成员。

      例如,可以调用基类的方法或者使用基类的属性。如果没有基类约束,编译器就无法知道某个类型实参拥有哪些成员。通过提供基类约束,编译器将知道所有的类型实参都拥有由指定基类所定义的成员。

    (2)确保类型实参支持指定的基类类型参数。这意味着对于任意给定的基类约束,类型实参必须要么是基类本身,要么是派生于该基类的类,如果试图使用没有继承指定基类的类型实参,就会导致编译错误。
    基类约束使用下面形式的where子句:
    where T:base-class-name
    T是类型参数的名称,base-class-name是基类的名称,这里只能指定一个基类。

1.2、 接口约束
  接口约束用于指定某个类型参数必须应用的接口。接口的两个主要功能和基类约束完全一样。
  基本形式 where T:interface-name
  interface-name是接口的名称,可以通过使用由逗号分割的列表来同时指定多个接口。
  如果某个约束同时包含基类和接口,则先指定基类列表,再指定接口列表。

1.3、 new()构造函数约束
  new()构造函数约束允许开发人员实例化一个泛型类型的对象。
  一般情况下,我们无法创建一个泛型类型参数的实例。然而,new()约束改变了这种情况,他要求类型参数必须提供一个无参数的构造函数。
  在使用new()约束时,可以通过调用该无参构造函数来创建对象。
  基本形式: where T : new()
  使用new()约束时应注意两点:
  (1)它可以与其他约束一起使用,但是必须位于约束列表的末端。
  (2)new()仅允许开发人员使用无参构造函数来构造一个对象,即使同时存在其他的构造函数。换句话说,不允许给类型参数的构造函数传递实参。

1.4 、引用类型和值类型约束
  如果引用类型和值类型之间的差别对于泛型代码非常重要,那么这些约束就非常有用。
  基本形式:
  where T : class
  where T : struct
  若同时存在其他约束的情况下,class或struct必须位于列表的开头。

  另外可以通过 使用约束来建立两个类型参数之间的关系
  例如 class GenericClass2<T, V> where V:T{} -------- 要求V必须继承于T,这种称为裸类型约束(naked type constraint)

  

  举例:

  class BaseC

  {

         int baseInt;

          string baseStr;

          public void Show()

          {

             Console.WriteLine("Something");

    }

  }

  

  //基类约束

  class GenericClass3<T> where T : BaseC

  {

          T ob1;

          public void Show()

          {

              ob1.Show();

          }

  }

  //new()构造函数约束

  class GenericClass4<T> where T : new()

  {

          public T ob1;

          public GenericClass4()

          {

              ob1 = new T();

          }

  }

1.5、default 关键字

  泛型代码中的默认关键字

  在泛型类和泛型方法中产生的一个问题是,在预先未知以下情况时,如何将默认值分配给参数化类型 T:

  T 是引用类型还是值类型。

  如果 T 为值类型,则它是数值还是结构。

  给定参数化类型 T 的一个变量 t,只有当 T 为引用类型时,语句 t = null 才有效;只有当 T 为数值类型而不是结构时,语句 t = 0 才能正常使用。解决方案是使用 default 关键字,此关键字对于引用类型会返回空,对于数值类型会返回零。对于结构,此关键字将返回初始化为零或空的每个结构成员,具体取决于这些结构是值类型还是引用类型。

 

2、使用泛型类

  在给泛型类传递类型实参的时候,实际创建的是C#中的“封闭构建类型”(closed constructed type)。

  术语“封闭”一词,是指指定了类型实参,因此GenericClass<int>是一个封闭构建类型。

  本质上,泛型类型,例如GenericClass<T>,是一种抽象结构。只有在特定的版本(例如GenericClass<int>)被构建以后才创建了一个实际的类型。

  在C#中术语中,GenericClass<T>之类的构造被称为“开放构建类型”(open constructed type),是因为他没有指定类型实参。

  举例:

  public void Demo1()

  {

         GenericClass<int,string> gen=new GenericClass<int,string>(12,"I am Chinese");

         Console.Write("Type: ");

         gen.ShowType();

         Console.WriteLine("The first Data: " + gen.GetData());

  }

  //new()构造函数约束

  public void Demo2()

  {

         GenericClass4<BaseC> demo4 = new GenericClass4<BaseC>();

         demo4.ob1.Show();

  }

 

  总结:

  ①:default

      在泛型中,要为某个使用泛型的变量初始化值,可是我们需要考虑的是这个泛型可能是引用类型,也可能是值类型,这时我们可以借助default来完成初始化复制。T value = default(T);如果T是引用类型,value = null,如果是T是值类型,value = 0.

  ②:where

    

     (注:在CRL2.0中,只能为默认构造函数定义约束,不能为其他构造函数定义约束)

3、.泛型继承

   如何实现C#泛型类的继承呢?这里需要满足下面两点中的任何一点即可:

  3.1、泛型类继承中,父类的类型参数已被实例化,这种情况下子类不一定必须是泛型类;

  3.2、父类的类型参数没有被实例化,但来源于子类,也就是说父类和子类都是泛型类,并且二者有相同的类型参数;

  //如果这样写的话,显然会报找不到类型T,S的错误  

  public class TestChild : Test< T, S> { }  

  //正确的写法应该是  

  public class TestChild : Test< string, int>{ } 

  public class TestChild< T, S> : Test< T, S> { }

posted @ 2021-03-04 11:31  方寸山学习  阅读(401)  评论(0)    收藏  举报