原文:http://en.csharp-online.net/Generic_types
内容:
1、为什么使用泛型?
2、泛型等价
3、专业述语
3.1、类型参数
3.2、类型引用
3.3、开放类型
3.4、构造类型
3.5、开放构造类型
3.6、关闭构造类型
4、基于类型参数的约束
5、泛型类
6、泛型接口
6.1、定义一个泛型接口
7、泛型委托
为什么使用泛型?
对于使用泛型,这里主要有两个主要原因来说明一下:
性能:通过使用装箱和拆箱数据类型来存储对象。这会带来相当大的系统开销,对于性能会带来打击。而通过使用泛型,就会消除这个性能上的问题。
类型安全:没有一个强类型信息在编译类型可以存储在集合中.为了更好地了解这些,下面有一个没有使用泛型的例子:
![]()
Code
1
public class Stack
2![]()
![]()
{
3
object[] store;
4
int size;
5![]()
public void Push(object obj)
{
}
6![]()
public object Pop()
{
}
7
}
为了使用这个类,可以将任何对象入栈,然而当检索对象时,需要显示的类型转换.
![]()
Code
1
Stack s = new Stack();
2
s.Push(28);
3
int j = (int)s.Pop(); //unboxing with explicit int casting
伴随着装箱和拆箱操作会增加性能开销,因为包含有动态内存分配和运行时的类型检测.
现在猜想会发生什么,如果根据以下代码将一个字符串入栈:
![]()
Code
1
Stack s = new Stack();
2
s.Push("Hello World!"); // pushing the string
3
int i = (int)s.Pop(); // run-time exception will be thrown at this point
代码会被编译,然而直到代码被执行,问题才变得可见,这里会抛出一个InvalidCastException异常.
泛型等价
如果以上Stack类是一个泛型类型,当将字符串入栈,会产生编译时的错误.如下是Stack类的泛型版本:
![]()
Code
1
public class Stack<T>
2![]()
![]()
{
3
// Items are of type T, which is known when you create the object
4
T[] items;
5
int count;
6![]()
public void Push(T item)
{
}
7
// The type of the method Pop will be decided when you create the object
8![]()
public T Pop()
{
}
9
}
T是一个参数类型,而在类里面可以将T作为一个类型来使用,它可以代表任何类型,包括一个简单的数据类型int,甚至一个复杂对象car.
根据以下例子,给出的string作为类型参数T.
![]()
Code
1
Stack<string> s = new Stack<string>();
2
s.Push("Hello World!");
3
string str = s.Pop();
Stack<string>被称为构造类型,在Stack<string>类型,每一个T的发生代表着类型参数string,Stack<string>的Push和Pop方法操作字符串值,当将其他类型的值入栈,会产生一个编译时错误,剔除显示转换的需要,当它们被检索时,返回它们最初的类型。
可以使用参数化,不仅用于类,而且还可以用于接口、结构、方法和委托。
![]()
Code
1
interface IComparable <T>
2
3
struct HashBucket <K,D>
4
5
static void Sort <T> (T[] arr)
6
7
delegate void Action <T> (T arg)
专业述语
当谈论泛型时,有若干依据会被使用,因此这值得提及它们,以便当阅读其他文档,可以获得更好的理解.
类型参数(Type parameters)
一个类型参数引用在泛型定义中被使用的参数。以上Stack类的泛型版本,这个类接受一个类型参数T。
类型引用(Type arguments)
一个类型引用,引用你指定来使用代替类型参数的参数。以下是代码片段,string是类型引用。
![]()
Code
1
Stack<string> s = new Stack<string>();
开放类型
代替作为一个类而引用,泛型认为Stack<T>作为一个开放类型。这个述语"open"意味着传送一个类型不完全定义的想法,对于采取多种的现实表现是"开发"的。
构造类型
一个构造类型代表着一个开放类型的实例。从开放Stack类型来创建一个构造类型。使用下面的代码:
或者
![]()
Code
1
Stack<string> s = new Stack<string>();
开放构造类型
当至少一个类型参数还没有被指定时,一个开放构造类型被创建,参数对于运行时的定义仍然开放。思考下面的类型声明:
![]()
Code
1![]()
public class LinkedList<K,T>
{
}
下面全部是开放构造类型:
![]()
Code
1
LinkedList<K,T> myList_1;
2
LinkedList<int,T> myList_2;
3
LinkedList<K,string> myList_3;
关闭构造类型
通过指定所有类型参数来创建关闭构造类型,因此它们对于运行时定义是不开放的。 思考下面的类型声明:
![]()
Code
1![]()
public class LinkedList<K,T>
{
}
下面全部是关闭构造类型:
![]()
Code
1
LinkedList<int,string> myList_1;
2
LinkedList<int,object> myList_2;
3
LinkedList<object,string> myList_3;
基于参数类型的约束
约束条件常常限制一个各种类型的泛型类。而客户端代码可以使用它(泛型类)作为类型参数。如果代码尝试使用不受约束的类型来实例化泛型类,结果却导致产生编译错误。约束条件通过where关键字来指定。以下是五种约束类型的表格清单:
约束条件 描述
where T: struct 类型参数,T,必需是除了可空类型的任何值类型。
where T: classs 类型参数,T,必需是一个引用类型,包括任何类、接口、委托或者数组类型。
where T:new() 类型参数,T,必需拥有一个公共没有参数的构造函数。当与其他约束条件连合使用,new()约束条件必需最后指定。
where T : <base class name> 类型参数,T,必需是派生自指定的基类。
where T:<interface name> 类型参数,T,必需实现指定的接口,多个接口约束可以被指定。约束接口也可以是泛型。
where T:U 类型参数,T,必需是派生自提供U参数。这被称为裸露的类型约束。
泛型类
泛型类封装不指定特殊数据类型的操作。泛型类对于其他类可以是基类,因此可以定义若干个虚方法或者抽象方法。任何派生类型必需按照规则遵守,确定泛型抽象的本质流向它。
如果一个不是泛型的类继承泛型类,派生类必需指定一个类型参数。
![]()
Code
1
// Generic list class.
2
public class GenericList<T>
3![]()
![]()
{
4
private List<T> myList = new List<T>();
5
}
6
7
// Derived type specifies the type parameter.
8
public class MyStringList : GenericList<string>
9![]()
![]()
{
10
}
如果一个泛型基类定义泛型虚方法或者抽象方法,派生类必需通过使用指定的参数类型来覆写泛型方法。
![]()
Code
1
// Generic class with virtual method.
2
public class GenericList<T>
3![]()
![]()
{
4
private List<T> myList = new List<T>();
5
6
public virtual void PrintList(T data)
7![]()
{
8
}
9
}
10
11
public class MyStringList : GenericList<string>
12![]()
![]()
{
13
// Derived method must substitute the type parameter
14
public override void PrintList(string data)
15![]()
{
16
}
17
}
一个泛型派生类型可以重新使用自己本身定义的类型占位符.设定在基类的任何约束必需通过派生类型被honoured.
![]()
Code
1
// Default constructor constraint.
2
public class GenericList<T> where T : new()
3![]()
![]()
{
4
private List<T> myList = new List<T>();
5
6
public virtual void PrintList(T data)
7![]()
{
8
}
9
}
10
11
// Derived type must honour constraint.
12
public class MyReadOnlyList<T> : GenericList<T> where T : new()
13![]()
![]()
{
14
public override void PrintList(T data)
15![]()
{
16
}
17
}
伴随着泛型类,它更好使用泛型接口,例如IComparable<T>而不是IComparable,为了逃避值类型的装箱和拆箱.当一个接口在类型参数作为一个约束而被指定.仅仅实现该接口的类型能够使用.例如:
![]()
Code
1
public class SortedList<T> : GenericList<T> where T : System.IComparable<T>
2![]()
![]()
{
3
. . .
4
}
定义一个泛型接口
你可以跟定义泛型类一样来定义泛型接口(包括约束):
![]()
Code
1
// Standard Interface
2
public interface IPrint
3![]()
![]()
{
4
void Print();
5
}
6
7
// Generic Interface
8
public interface MyGenericInterface<T> where T : IPrint
9![]()
![]()
{
10
void Run(T t);
11
}
12
13
// Class that implements standard Interface
14
public class MyPrinter : IPrint
15![]()
![]()
{
16
public void Print()
17![]()
{
18
Console.WriteLine("hello");
19
}
20
}
21
22
// Class that implements Generic Interface
23
public class Print2 : MyGenericInterface<MyPrinter>
24![]()
![]()
{
25
public void Run(MyPrinter t)
26![]()
{
27
t.Print();
28
}
29
}
30
31
// Generic Class that implements Generic Interface with contraints
32
public class PrintDemo<T> : MyGenericInterface<T> where T : IPrint
33![]()
![]()
{
34
public void Run(T t)
35![]()
{
36
t.Print();
37
}
38
}
.NET Framework 包括所有通用接口的泛型版本。
泛型委托
假设我们想委托可以更新某一项,但是不确定准备更新哪一项。在这样的情形下,使用
泛型委托是一个最好的选择。为了解释这个概念,以下的代码是基于来自Tod Golding
in .Net 2.0 Generics的一个例子。
假设你将会想象出,我们正在创建一个关于动物,称为猫和狗的软件项目的片段。因此
这里有一些属性和一个ToString()方法的两个类:
![]()
Code
1
public class Cat
2![]()
![]()
{
3
private string m_name;
4
public string Name
5![]()
{
6![]()
get
{ return m_name; }
7![]()
set
{ m_name = value; }
8
}
9
10
private int m_age;
11
public int Age
12![]()
{
13![]()
get
{ return m_age; }
14![]()
set
{ m_age = value; }
15
}
16
17
public Cat(string name, int age)
18![]()
{
19
m_name = name;
20
m_age = age;
21
}
22
23
public override string ToString()
24![]()
{
25
return string.Format("The Cat named {0} is {1} years old",
26
m_name.ToString(), m_age.ToString());
27
}
28
}
29
30
public class Dog
31![]()
![]()
{
32
private string m_name;
33
public string Name
34![]()
{
35![]()
get
{ return m_name; }
36![]()
set
{ m_name = value; }
37
}
38
39
private double m_age;
40
public double Age
41![]()
{
42![]()
get
{ return m_age; }
43![]()
set
{ m_age = value; }
44
}
45
46
public Dog(string name, double age)
47![]()
{
48
m_name = name;
49
m_age = age;
50
}
51
52
public override string ToString()
53![]()
{
54
return string.Format("The Dog named {0} is {1} years old",
55
m_name.ToString(), m_age.ToString());
56
}
57
}
我们认为委托能够更新猫和狗其中一个的年龄。
![]()
Code
1
public delegate void UpdateAnimal<T>(T value);
假如我们将会拥有许多动物,采用集合将会是一个不错的办法,而且又能够节省开销,给出一个类型安全的泛型集合将会是最好的选择。
![]()
Code
1
public class Animals<T> : List<T>
2![]()
![]()
{
3
public void UpdateAnimals(UpdateAnimal<T> updater)
4![]()
{
5
List<T>.Enumerator items = GetEnumerator();
6
7
while (items.MoveNext())
8![]()
{
9
updater(items.Current);
10
}
11
}
12
13
public void Print()
14![]()
{
15
List<T>.Enumerator items = GetEnumerator();
16
17
while (items.MoveNext())
18![]()
{
19
Console.WriteLine(items.Current.ToString());
20
}
21
}
22
}
最后,我们需要两个被委托调用的方法,来更新每个类。以下使用一些代码来进行测试。
![]()
Code
1
class Program
2![]()
![]()
{
3
static void Main(string[] args)
4![]()
{
5
// Create Lists
6
Animals<Cat> catList = new Animals<Cat>();
7
catList.Add(new Cat("Tinkerbelle", 6));
8
catList.Add(new Cat("Felix", 3));
9
catList.Add(new Cat("Whiskers", 10));
10
catList.Add(new Cat("Tailz", 14));
11
12
Animals<Dog> dogList = new Animals<Dog>();
13
dogList.Add(new Dog("Rufus", 12.1));
14
dogList.Add(new Dog("Peeps", 3.2));
15
dogList.Add(new Dog("Hairy McClary", 6.3));
16
17
18
// Cats
19
catList.Print();
20
Console.WriteLine("---------------------------");
21
22
catList.UpdateAnimals(new UpdateAnimal<Cat>(UpdateCatAge));
23
catList.Print();
24
25
Console.WriteLine("===========================");
26
27
// Dogs
28
dogList.Print();
29
Console.WriteLine("---------------------------");
30
31
dogList.UpdateAnimals(new UpdateAnimal<Dog>(UpdateDogAge));
32
dogList.Print();
33
}
34
35
public static void UpdateCatAge(Cat cat)
36![]()
{
37
cat.Age += 1;
38
}
39
40
public static void UpdateDogAge(Dog dog)
41![]()
{
42
dog.Age += 0.1;
43
}
44
}
输出结果如下:
![]()