zl程序教程

您现在的位置是:首页 >  后端

当前栏目

【C#】范型详解

c# 详解
2023-09-11 14:14:50 时间


借助泛型,你可以根据要处理的精确数据类型定制方法、类、结构或接口。 例如,不使用允许键和值为任意类型的 Hashtable 类,而使用 Dictionary<TKey,TValue> 泛型类并指定允许的密钥和值类型。 泛型的优点包括:代码的可重用性增加,类型安全性提高。

定义和使用泛型

泛型是为所存储或使用的一个或多个类型具有占位符(类型形参)的类、结构、接口和方法。 泛型集合类可以将类型形参用作其存储的对象类型的占位符;类型形参呈现为其字段的类型和其方法的参数类型。 泛型方法可将其类型形参用作其返回值的类型或用作其形参之一的类型。 以下代码举例说明了一个简单的泛型类定义。

public class Generic<T>
{
    public T Field;
}

创建泛型类的实例时,指定用于替代类型形参的实际类型。 在类型形参出现的每一处位置用选定的类型进行替代,这会建立一个被称为构造泛型类的新泛型类。 你将得到根据你选择的类型而定制的类型安全类,如以下代码所示。

public static void Main()
{
    Generic<string> g = new Generic<string>();
    g.Field = "A string";
    //...
    Console.WriteLine("Generic.Field           = \"{0}\"", g.Field);
    Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName);
}

泛型术语

泛型类型定义 是用作模板的类、结构或接口声明,带有可包含或使用的类型的占位符。 例如, System.Collections.Generic.Dictionary<TKey,TValue> 类可以包含两种类型:密钥和值。 由于泛型类型定义只是一个模板,所以你无法创建作为泛型类型定义的类、结构或接口的实例。

泛型类型参数(或类型参数)是泛型类型或方法定义中的占位符。 System.Collections.Generic.Dictionary<TKey,TValue> 泛型类型具有两个类型形参 TKey 和 TValue,它们分别代表密钥和值的类型。

构造泛型类型(或 构造类型)是为泛型类型定义的泛型类型形参指定类型的结果。

泛型类型实参 是被泛型类型形参所替代的任何类型。

常见术语泛型类型包括构造类型和泛型类型定义。

借助泛型类型参数的协变和逆变,可以使用类型自变量的派生程度比目标构造类型更高(协变)或更低(逆变)的构造泛型类型。 协变和逆变统称为“变体” 。

约束是对泛型类型参数的限制。 例如,你可能会将一个类型形参限制为实现 System.Collections.Generic.IComparer 泛型接口的类型,以确保可对该类型的实例进行排序。 此外,你还可以将类型形参限制为具有特定基类、具有无参数构造函数或作为引用类型或值类型的类型。 泛型类型的用户不能替换不满足约束条件的类型实参。

泛型方法定义 是具有两个形参列表的方法:泛型类型形参列表和形参列表。 类型形参可作为返回类型或形参类型出现,如以下代码所示。

T Generic<T>(T arg)
{
    T temp = arg;
    //...
    return temp;
}

泛型方法可出现在泛型或非泛型类型中。 值得注意的是,方法不会仅因为它属于泛型类型或甚至因为它有类型为封闭类型泛型参数的形参而成为泛型方法。 只有当方法有属于自己的类型形参列表时才是泛型方法。 在以下代码中,只有方法 G 是泛型方法。

class A
{
    T G<T>(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}
class Generic<T>
{
    T M(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}

泛型类的五大约束

  1. where T:struct 限定当前参数类型必须是值类型
  2. where T:class 限定当前类型参数类型必须是引用类型
  3. where T:new() 限定当前参数类型必须有一个无参构造器
  4. where T:<base class name>> 限定当前参数类型 必须是当前类 或者当前类为基类的类
  5. where T:<interface name>>限定当前参数类型必须实现指定接口
//泛型五大类型约束
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.Threading.Tasks;
namespace  泛型的五大约束
{
     泛型类的五大约束
     // 1. where T:struct  限定当前参数类型必须是值类型
     // 2. where T:class   限定当前类型参数类型必须是引用类型
     // 3. where T:new() 限定当前参数类型必须有一个无参构造器
     // 4. where T:<base class name>   限定当前参数类型 必须是当前类  或者当前类为基类的类
     //父类名
     // 5. where T:<interface name>限定当前参数类型必须实现指定接口 
     class  Program
    {
         static  void  Main( string [] args)
        {
             //D<Base> d = new D<Base>();
             //Base ba = new Base();
             //Console.WriteLine("泛型类D的  funcd的方法  在此被调用");
             //d.funcd(ba);//此处调用funcd的方法
             //C<Cb> c = new C<Cb>();
             //c.f();
             F  f =  new  F ();
             E < F > e =  new  E < F >();
            e.funce(f);
        }
    }
     public  class  Base
    {
         public  void  fb()
        {
             Console .WriteLine( "这是基类Base的方法fb" );
        }
    }
     public  class  A < T > where  T : struct
    {
         public  void  funca( T  a)
        {
        }
    }
     public  class  B
    {
    }
     public  class  C < T >  where  T  : new ()
    {
         public  void  f()
        {
             Console .WriteLine( "这里是无参构造函数的方法" );
        }
    }
     public  class  Cb
    {
         public  Cb()
        {
        }
      
    }
     public  class  D < T > where  T  : Base
    {
         public  void  funcd( T  a)
        {
            a.fb();
        }
    }
     public  interface  INter
    {
         int  funinter();
    }
     public  class  F : INter
    {
         public    int  funinter()
        {
             Console .WriteLine( "这里是实现了继承接口内的方法" );
             return  0;
        }
    }
     public  class  E < T >  where  T : INter // 第五种类型
    {
         public  T  funce( T  a)
        {
            a.funinter();
             Console .WriteLine( "这里是E内的方法的实现" );
             return  a;
         
        }
    }
   
}

泛型的利与弊

  • 类型安全。 泛型将类型安全的负担从你那里转移到编译器。 没有必要编写代码来测试正确的数据类型,因为它会在编译时强制执行。降低了强制类型转换的必要性和运行时错误的可能性。
  • 代码更少且可以更轻松地重用代码。 无需从基类型继承,无需重写成员。 例如,可立即使用 LinkedList 。
    例如,你可以使用下列变量声明来创建字符串的链接列表:
LinkedList<string> llist = new LinkedList<string>();
  • 性能更好。 泛型集合类型通常能更好地存储和操作值类型,因为无需对值类型进行装箱。
  • 泛型委托可以在无需创建多个委托类的情况下进行类型安全的回调。 例如, Predicate
    泛型委托允许你创建一种为特定类型实现你自己的搜索标准的方法并将你的方法与 Array 类型比如 Find、 FindLast和 FindAll方法一起使用。
  • 泛型简化动态生成的代码。 使用具有动态生成的代码的泛型时,无需生成类型。
    这会增加方案数量,在这些方案中你可以使用轻量动态方法而非生成整个程序集。

以下是泛型的一些局限:

  • 泛型类型可从多数基类中派生,如 MarshalByRefObject (约束可用于要求泛型类型形参派生自诸如
    MarshalByRefObject的基类)。 不过,.NET 不支持上下文绑定的泛型类型。 泛型类型可派生自
    ContextBoundObject,但尝试创建该类型实例会导致 TypeLoadException。
  • 枚举不能具有泛型类型形参。 枚举偶尔可为泛型(例如,因为它嵌套在被定义使用 Visual Basic、C# 或 C++ 的泛型类型中)。
  • 轻量动态方法不能是泛型。
  • 在 Visual Basic、C# 和 C++ 中,包含在泛型类型中的嵌套类型不能被实例化,除非已将类型分配给所有封闭类型的类型形参。 另一种说法是:在反射中,定义使用这些语言的嵌套类型包括其所有封闭类型的类型形参。 这使封闭类型的类型形参可在嵌套类型的成员定义中使用。

引用

.NET 中的泛型