C# 泛型整理

一、什么是泛型?

     泛型是 2.0 版 C# 语言和公共语言运行库 (CLR) 中的一个新功能。它将一个或多个类型的指定推迟到客户端代码声明并实例化该类或方法的时候。在功能上,泛型类似于模板,可以在需要时为这个模板传入任何我们需要的类型。

二、泛型的特点

  • 使用泛型类型可以最大限度地重用代码、保护类型的安全以及提高性能。
  • 可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
  • 可以对泛型类进行约束以访问特定数据类型的方法。
  • 关于泛型数据类型中使用的类型的信息可在运行时通过反射获取。
  • 泛型最常见的用途是创建集合类。

三、泛型初步了解

    //冒泡排序方法泛型示例
    public class SortHelper  //排序类
    {
        public void sort<T>(T[] array) where T : IComparable<T>   //泛型排序方法
        {
            int len = array.Length;

            for (int i = 0; i <= len - 2; i++)
            {
                for (int j = len - 1; j >= 1; j--)
                {
                    if (array[j].CompareTo(array[j - 1]) < 0)  //必须用CompareTo比较大小
                    {
                        T temp = array[j];
                        array[j] = array[j - 1];
                        array[j - 1] = temp;
                    }
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            SortHelper sh = new SortHelper();

            int[] intArray = new int[] {6, 8, 2, 9, 4 };

            sh.sort<int>(intArray);  //int类型数据排序

            for (int i = 0; i < intArray.Length; i++)
            {
                Console.Write(intArray[i]);
            }

            byte[] byteArray = new byte[] { 6, 8, 2, 9, 4 };

            sh.sort<byte>(byteArray);  //byte类型数据排序

            for (int i = 0; i < byteArray.Length; i++)
            {
                Console.Write(byteArray[i]);
            }

            Console.ReadKey();
        }
    }  
Note:
  • 在.net中,我们将示例的T占位符称为“类型参数”,当然,类型参数的命名可以自己定义,但我们最好遵循它的命名的准则:1、考虑使用 T 作为具有单个字母类型参数的类型的类型参数名。2、务必将“T”作为描述性类型参数名的前缀。3、考虑在参数名中指示对此类型参数的约束。例如,可以将带有ISession 约束的参数命名为TSession
  • 在示例中比较array[j]与array[j-1]的大小时,不能直接用'>'、'<'、'==','!='等比较数值,因为array类型的确定要在SortHelper 类实例化之后,而我们编译时还不知道array类型,所以编译会出错,我们必须实现IComparable接口:public interface IComparable {int CompareTo(object obj);},也就是限制T类型参数只能是实现了IComparable<T>接口 的类型,如int、byte等等,而我们就可以调用该类型的CompareTo()方法进行数值比较。
  • 示例中sh.sort<int>(intArray); 可以改成sh.sort(intArray);,因为调用泛化的方法可以省略类型参数,编译器会自动推断出该参数。

四、泛化类型

     (1)类泛化  

       //基类继承方式:
       class BaseNode { }
       class BaseNodeGeneric<T> { }
       class NodeConcrete<T> : BaseNode { }     //从具体的构造基类继承
       class NodeClosed<T> : BaseNodeGeneric<int> { }  //从封闭式构造基类继承
       class NodeOpen<T> : BaseNodeGeneric<T> { }   //从开放式构造基类继承
 
       //非泛型(具体)类可以从封闭式构造基类继承,但无法从开放式构造类或裸类型参数继承。
       class Node1 : BaseNodeGeneric<int> { }   //正确
       class Node2 : BaseNodeGeneric<T> {}   //编译出错,客户端代码无法提供实例化基类所需的类型变量
       class Node3 : T {}  //编译出错

       //从开放式构造类型继承的泛型类必须为任何未被继承类共享的基类类型参数提供类型变量。
       class BaseNodeMultiple<T, U> { }
       class Node4<T> : BaseNodeMultiple<T, int> { }  //正确
       class Node5<T, U> : BaseNodeMultiple<T, U> { }  //正确
       class Node6<T> : BaseNodeMultiple<T, U> {}   //编译出错,因为Node6无法提供实例化基类所需的U类型变量

       //从开放式构造类型继承的泛型类必须指定约束,这些约束是基类型约束的超集或暗示基类型约束。
       class NodeItem<T> where T : System.IComparable<T>, new() { }
       class SpecialNodeItem<T> : NodeItem<T> where T : System.IComparable<T>, new() { } //指定基类的约束

       //泛型类型可以使用多个类型参数和约束。
       class SuperKeyType<K, V, U> where U : System.IComparable<U> where V : new(){ }

       //示例:       
       public class SortHelper<T> where T: IComparable<T>{}
       SortHelper<int> sh = new SortHelper<int>();
       int[] intArray = new int[] {6, 8, 2, 9, 4 };
       sh.sort(intArray);   
      (2)方法泛化
       //泛型方法的类型参数不能与类的类型参数相同,但非泛型方法可以访问类级别类型参数
       class GenericList<T>
       {
            void SampleMethod<T>() { } //编译出错
            void SampleMethod<U>() { } //正确
            void SampleMethod(T array) { } //正确
       }

       //泛型方法可以使用许多类型参数进行重载。
       class GenericList
       {
           void DoWork() { }
           void DoWork<T>() { }
           void DoWork<T, U>() { } 
       }

      //示例见上面冒泡排序方法泛型

      注意:不能仅因为某个方法属于泛型类型且使用泛型类型的类型参数,就称该方法是泛型方法。只有当方法具有自己的类型参数列表时,才能称其为泛型方法。

     (3)接口泛化

      //可将多重接口指定为单个类型上的约束
      class Stack<T> where T : System.IComparable<T>, IEnumerable<T>{}

      //类之间的继承规则同样适用于接口,一个接口可定义多个类型参数。
      interface IMonth<T,U> { }
      interface IJanuary     : IMonth<int,int> { }  //No error
      interface IFebruary<T> : IMonth<int,int> { }  //No error
      interface IMarch<T>    : IMonth<T,int> { }    //No error
      interface IApril<T>  : IMonth<T, U> {}  //Error
      (4)委托泛化
      //示例:
      public delegate void myDelegate<T>(T item);
      public static void display(int value){}
      myDelegate<int> md = new myDelegate<int>(display);

      //C# 2.0 版具有称为方法组转换的新功能,此功能适用于具体委托类型和泛型委托类型,上面示例可以简化:
      myDelegate<int> md = display;
五、类型参数约束

       在定义泛型类型时可以对客户端代码在实例化类时用于类型参数的类型加以限制。约束使用 where 关键字指定。

  • where T : struct       限制类型参数T必须是值类型,即继承自System.ValueType。
  • where T : class       限制类型参数T必须是引用类型,包括任何类、接口、委托或数组类型。
  • where T : new()      限制类型参数T必须有一个缺省的构造函数(public且无参的构造函数),当与其他约束一起使用时,new() 约束必须最后指定。
  • where T: <base classname>     类型参数必须继承至指定的基类(base class)
  • where T: <interface name>     类型参数必须是指定的接口或实现了指定接口的类型,可以指定多个接口约束。
  • where T: U     为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数,这称为裸类型约束。
    //构造器约束
    class A{public A(){}}
    class B{public B(int value){}}
    class C<T>where T:new(){    }
    C<A> c = new C<A>(); //正确,A有无参构造函数
    C<B> c = new C<B>(); //编译错误,B没有无参构造函数

    //值类型/引用类型约束
    public struct A{}
    public class B{}
    class C<T> where T:struct{}
    C<A> c = new C<A>(); //正确,A是一个值类型
    C<B> c = new C<B>(); //编译错误,B是一个引用类型

    //基类约束
    class A{}
    class B{}
    class C<S,T>where S:A where T:B{
       //两者继承不同的类,可调用该类的方法
    }

        使用约束可以使用我们对泛型成员执行操作时变得更加安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜之子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值