泛型的简单介绍
本文使用一个简单的示例来说明泛型在 C# 2.0 中的重要性。
引言
本文使用一个简单的列表场景来说明泛型的重要性。
背景
C# 2.0 是一种功能强大的语言,具有一些有趣的特性,包括分部类、可空类型、匿名方法、泛型等等。
在所有这些特性中,我认为泛型尤其有趣,尤其是在处理列表或项目集合时。
在本文中,我将使用一个简单的列表场景来展示泛型的重要性。
简单定义
泛型是代码模板,允许开发人员在不引用特定数据类型的情况下创建类型安全的代码。
简单来说,泛型是具有占位符的类、结构、接口和方法,这些占位符用于它们存储或使用的一个或多个类型。
简单列表场景
考虑一个场景,您有两个值列表,即 `list1` 和 `list2`,其中 `list1` 包含整数值,`list2` 包含 `string`,如下所示:
List1 = {1,2,3,4}
List2 = {"string1","string2","string3"}
现在假设您想开发一个类,该类可用于存储任一列表项(`list1` 项或 `list2` 项)。
由于在 C# 中,整数和 `string` 都可以被视为“对象”,因此您决定您的类将存储一个对象数组,所以您创建了下面的代码中的类。
用作对象集合的类
// Use this class to store a collection of objects
public class MyObjCollection
{
object[] items = new object[50]; // limit items
int numitems = 0;
public void Add(object item)
{
if (numitems + 1 < 50)
{
items[numitems] = item;
numitems++;
}
}
// This enables to retrieve an item by using mygencollection[2];
public object this[int n]
{
get { return items[n]; }
}
}
现在,要使用此类存储 `list1` 项(整数),我们使用以下代码:
MyObjCollection objintcollection = new MyObjCollection();
objintcollection.Add(1);
objintcollection.Add(2);
objintcollection.Add(3);
objintcollection.Add(4);
注意:由于 `Add(object item)` 方法期望一个对象,因此除了整数之外,我们还可以将其他数据类型添加到此集合中。例如:
objintcollection.Add("string 1");
现在,如果我们想存储 `list2` 项,我们将有类似的代码,只是我们将实例命名不同:
MyObjCollection objstringcollection = new MyObjCollection();
objstringcollection.Add("string1");
objstringcollection.Add("string2");
objstringcollection.Add("string3");
系统仍然允许我们将非 `string` 值添加到此集合中,例如:
objstringcollection.Add(3);
类型安全
我们可以将非整数值添加到存储 `list1` 值的集合中,也可以将非 `string` 值添加到存储 `list2` 值的集合中,这表明我们的集合不是类型安全的。
类型转换
要从任何这些集合中检索值,必须进行类型转换,例如:
Int value1 = (int)objintcollection[0];
或者
string str1 = (string)objstringcollection[0];
这是一个问题,因为类型转换会降低性能。
InvalidCastExceptions (无效类型转换异常)
即使您将整数值转换为 `string` 或反之亦然,C# 编译器也会始终编译;但是,您会在运行时收到一个异常。例如,以下代码会编译:
string value1 = (string)objintcollection[0];
但是请记住,您将整数存储在集合中,因此在运行时您将收到一个 `InvalidCastException`。为了认识到泛型在处理列表时的重要性,让我们考虑一个泛型类,它存储一个给定数据类型的泛型类型数组,而不是对象数组,如下面的代码所示:
泛型类存储给定数据类型的值的集合。
public class MyGenCollection<T>
{
T[] items = new T[50];
int numitems = 0;
public void Add(T item)
{
if (numitems + 1 < 50)
{
items[numitems] = item;
numitems++;
}
}
// This enables to retrieve an item by using mygencollection[2];
public T this[int n]
{
get { return items[n]; }
}
}
注意:此类与对象集合类相似,但它没有对象集合,而是拥有一个泛型类型 `T` 的集合。在类声明中,即 `public class MyGenCollection<T>`,`<T>` 是一个占位符,`T` 是一个类型参数,将被替换为任何数据类型。假设您想使用上述类来存储 `list1` 项(整数),我们将使用以下代码:
MyGenCollection<int> intcollection = new MyGenCollection<int>();
intcollection.Add(1);
intcollection.Add(2);
intcollection.Add(3);
intcollection.Add(4);
注意:在这种情况下,`Add(int item)` 方法只接受 `int` 类型的值,因此添加任何其他数据类型都会导致编译时错误。例如,以下代码将无法编译:
intcollection.Add("string 1");
要存储 `list2` 项(`string`),我们将如下实例化类:
MyGenCollection<string> stringcollection = new MyGenCollection<string>();
stringcollection.Add("string1");
stringcollection.Add("string2");
stringcollection.Add("string3");
考虑到您向系统指定了要在集合中存储的值的数据类型,这使得泛型集合类类型安全。
无需类型转换
另请注意,您无需键入转换从集合中检索的任何值。例如,以下代码足以检索整数集合的第一个项:
int value1 = intcollection[0];
无 InvalidCastExceptions (无效类型转换异常)
由于没有进行数据类型转换,因此消除了无效类型转换异常。
泛型方法
方法也可以使用泛型类型参数定义,例如:
public void doAction<T>(T argument)
{}
您也可以拥有:
public T doAction<T>(T argument)
{}
`T` 是类型参数,在调用方法时可以指定,例如:
Myobject.doAction<int>(56);
注意:由于类型 `T` 是泛型的,因此在方法体内可能无法执行某些特定于数据类型的操作,例如以下代码将无法编译:
public T GetSum<T>(T arg1, T arg2)
{
return arg1+arg2;
}
但是,通过继承,可以声明一个泛型 `abstract` 方法,并在实现方法时传递一个数据类型,例如:
public abstract class MyClassA<T>
{
public abstract T GetDifference (T arg1, T arg2);
}
public class MyClassB: MyClassA<int> //integer type specified
{
public int GetDifference(int arg1, int arg2)
{
return arg1 - arg2;
}
}
结论
泛型特别有用,尤其是当您希望开发可随时与任何数据类型一起使用的代码模板时。泛型也适用于编程的其他概念,包括继承,例如泛型接口、反射、远程处理、委托等。开发人员掌握泛型的工作方式很重要,以便开发出可维护、可扩展且性能良好的高级解决方案。
Phillip Walera
系统分析师/开发人员
Sharepoint Africa 创始人
历史
- 2007 年 4 月 24 日:初次发布