C# 泛型是C# 2.0中最强大的功能。特点一:通过泛型可以定义类型安全的数据结构,而无需使用具体实际的数据类型,着能够显著提高性能并得到高质量的代码。在概念上,反省类似于C++模板,但是在实现和功能方面存在明显差异。特点二:泛型通过把类型参数化来达到代码重用的目标,这一特性可以应用在类、结构、接口、委托、方法的设计之中。泛型除了可以大幅提高代码复用性外,特点三:还可以提供编译期间的类型检查,减少不必要的显式类型转换,减少不必要的装箱操作,从而提高应用程序的运行效率。
1. 引入C# 泛型的原因:
一般情况下,在通用的数据结构中(例如Stack,List,Dictionory等)存储的数据,要求必须有相同的数据类型。如果必须存储不同类型的数据,那么唯一的方法就是将所有的数据首先装箱为object 类型,然后再存储。
例如,下面的Stack类将其所有的数据存储在一个object类型的数组中,该类型的两个方法分别使用object来获取和返回数据:
- public class Stack
- {
- object[] items;
- public void Push(object item) {...}
- public object Pop() {...}
- }
这样做的缺点:
第一是性能:根据装箱和堆栈的功能,使用Push方法能够想堆栈中压入任何类型的值,然而,再重新获取堆栈中的数据值时必须在使用Pop方法拆箱的同时,使用显式类型转换得到合适的数据类型。这种装箱和拆箱的操作增加了执行的负担,因为它带来了动态内存分配和运行时类型检查。
第二是类型安全。因为编译器允许在任何类型和Object之间进行强制类型转换,所以将造成编译时类型安全的不足。主要是Stack类无法强制设置堆栈中的数据类型。
第三是工作效率。编写类型特定的数据结构以及冗繁的的转换代码是一项乏味重复的且易于出错的工作 。
为了有效解决以上问题,c#2.0引入了泛型。
2.C# 泛型概述
泛型常见于集合应用中,在。net2.0框架的类库中,提供了一个新的名空间System.Collections.Generic,其中包含了一些新的基于泛型的容器类,例如,System.Collectinos.Generic.Stack,System.Collections.Dictionary,Collections.Generic.List,System.Collections.Generic.Queue
等,这些类库可以在集合中实现泛型。
以堆栈中实现泛型为例
Stack类示意代码:
- public class Stack﹤T﹥
- {
- T[] items;
- int count;
- public void Push(T item) {...}
- public T Pop() {...}
- }
Stack类应用代码:
- //实例化Stack类
- Ststem.Collections.Genric.Stack﹤string﹥ stringStack = new System.Collections.Generic.Stack﹤string﹥;
- //增加数据
- stringStack.Push("硬盘");
- stringStack.Push("声卡");
- stringStack.Push("电源");
- //转换为数组
- Araay stringArray;
- stringArray = stringStack.ToAraay();
- //显示数据
- foreach(string item in stringArray)
- {
- Console.WriteLine(item);
- }
3.C# 泛型之约束
为了提供更强大的编译期间的类型检查和减少类型转换,c#允许一个可选的为每个类型参数提供的约束列表。一个类型参数的约束指定了一个类型必须遵守的要求,使得这个类型参数能够作为一个变量来使用。约束有关键字where来声明,后跟类型参数的名字,再后是一个类或接口 类型的列表,或者构造器约束new().
比如要想使Dictionary﹤K,V﹥类能保证键值始终实现IComparable接口,类的声明中应该对类型参数K指定一个约束
- public class Dictionary﹤K,V﹥ where K: IComparable
- {
- public void Add(K key, V value)
- {
- ...
- if(key.CompareTo(x) ﹤ 0 ) {....}
- ...
- }
- }
类型参数约束的使用要小心。尽管它们提供了更强大的编译期间的类型检查并在一些情况下改进了性能,它还是限制了泛型类型的使用。比如一个泛型类List﹤T﹥可能约束T实现IComparable接口,以便Sort方法能够比较其中的元素。然而这么做的结果使得List﹤T﹥不能用于那些不能实现IComparable接口的类型,尽管在某些情况下Sort方法从没有被实际调用过。
4.C# 泛型类型的成员
- class C﹤V﹥
- {
- public V f1; //声明字段
- public D﹤V﹥ f2; //作为其他泛型类型 的参数
- public C﹤V x﹥
- {
- this.f1 = x;
- }
- }
泛型类型的成员可以使用泛型类型声明中的类型参数。但类型参数如果没有任何约束,则只能在该类型上使用从System.Object继承的共有成员。
5.C# 泛型接口
- interface IList﹤T﹥
- {
- T[] GetElements();
- }
- interface IDictionary﹤K,V﹥
- {
- void Add(K key,V value);
- }
- //泛型接口的类型参数要么已实例化
- //要么来源于实现类声明的类型参数
- class List﹤T﹥:IList﹤T﹥,IDictionary﹤int,T﹥
- {
- public T[] GetElements{}
- {
- return null;
- }
- public void Add(int index,T value){}
- }
6.C# 泛型委托
- delegate bool Predicate﹤T﹥(T value);
- class X
- {
- static bool F(int i){...}
- static bool G(string s){...}
- static void Main()
- {
- Predicate﹤string﹥ p2 = G;
- Predicate﹤int﹥ p1 = new Predicate﹤int﹥(F);
- }
- }
泛型委托支持返回值和参数哂纳感应用参数类型,这些参数类型同样可以附带合法的约束。
7.C# 泛型方法的简介
C#泛型机制只支持“在方法声明上包含类型参数” -- 即泛型方法。
C#泛型机制不支持在除方法外的其他成员(包括属性、事件、索引器、构造器、析构器)的声明上包含类型参数,但这些成员本身可以包含在泛型类型中,并使用泛型类型的类型参数。
泛型方法既可以包含在泛型类型中,也可以包含在非泛型类型中。
8.C# 泛型方法的声明与调用
- public class Finder
- {
- // 泛型方法的声明
- public static int Find﹤T﹥(T[] items,T item)
- {
- for(int i=0;i﹤items.Length;i++)
- {
- if(items[i].Equals(item)
- {
- return i;
- }
- }
- return -1;
- }
- }
- // 泛型方法的调用
- int i = Finder.Find﹤T﹥(new int[]{1,3,4,5,6,8,9},6);
泛型编程
9.C# 泛型方法的重载
- class MyClass
- {
- void F1﹤T﹥(T[] a,int i); // 不可以构成重载方法
- void F1﹤U﹥(U[] a,int i);
- void F2﹤T﹥(int x); // 可以构成重载方法
- void F2(int x);
- void F3﹤T﹥(T t) where T : A; // 不可以构成重载方法
- void F3﹤T﹥(T t) where T : B;
- }
10.C# 泛型方法的重写
- abstract class Base
- {
- public abstract T F﹤T,U﹥(T t,U u) where U : T;
- public abstract T G﹤T﹥(T t) where U : IComparable;
- }
- class Derived:Base
- {
- // 合法的重写,约束被默认继承
- public override X F(X,Y)(X x,Y y){}
- // 非法的重写,指定任何约束都是多余的
- public override T G﹤T﹥(T t) where T : Comparable{}
- }
11.C# 泛型约束简介
C#泛型要求对"所有泛型类型或泛型方法的类型参数"的任何假定,都要基于"显式的约束",以维护C#所要求的类型安全.
"显式约束"有where字句表达,可以指定"基类约束","接口约束","构造器约束","值类型/引用类型约束"共四中约束.
"显示约束"并非必须,如果没有指定"显式约束",泛型类型参数将只能访问System.Object类型中的公有方法.
基类约束
- class A
- {
- public void F1(){}
- }
- class B
- {
- public void F2(){}
- }
- class C(S,T)
- where S:A // S继承自A
- where T:B // T继承自B
- {
- // 可以在类型为S的变量上调用F1
- // 可以在类型为T的变量上调用F2
- }
接口约束
- interface IPrintable{coid Print();}
- interface IComparable﹤T﹥{int CompareTo(T v);}
- interface IKeyProvider﹤T﹥{T HetKey();}
- class Dictionary﹤K,V﹥
- where K:IComparable﹤K﹥
- where V:IPrintable,IKeyProvider﹤K﹥
- {
- // 可以在类型为K的变量上调用CompareTo
- // 可以在类型为V的变量上调用Print和GetKey
- }
构造器约束
- class A
- {
- public A(){}
- }
- class B
- {
- public B(int i)()
- }
- class C﹤T﹥
- where T:new()
- {
- // 可以在其中使用T t = new T();
- }
- 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
- {
- // T在这里面是一个值类型
- }
- C﹤A﹥ c = new C﹤A﹥(); // 可以,A是一个值类型
- C﹤B﹥ c = new C﹤B﹥(); // 错误,B是一个引用类型
12.C# 泛型总结
C# 泛型能力有CLR在运行时支持,它既不同于c++在编译时所支持的静态模板,也不同于java在编译器层面使用"檫拭法"支持的简单的类型.
C# 泛型支持包括类,结构,接口,委托共四种泛型类型,以及方法成员.
C# 泛型采用"基类,接口,构造器,值类型/引用类型"的约束方式来实现对类型参数的"显式约束",它不支持C++模板那样的基于签名的显式约束.
C# 泛型的基本知识就介绍到这里了,希望对你了解和学习C# 泛型有所帮助。
【编辑推荐】