65.9K
CodeProject 正在变化。 阅读更多。
Home

泛型的简单介绍

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.94/5 (10投票s)

2007年4月24日

CPOL

4分钟阅读

viewsIcon

38516

本文使用一个简单的示例来说明泛型在 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 日:初次发布
© . All rights reserved.