【oj每周推荐】谈谈C#中的泛型

我姓区不姓区 2009-06-22 09:48:08
加精
2.0 版 C# 语言和公共语言运行库 (CLR) 中增加了泛型。估计现在绝大多数人都用过了,而且用得最多的就是泛型集合List<T>,以至于现在一说到泛型,很多人的第一反应就是List<T>,在这里我要说说,把泛型等同于List<T>的乃是还没有真正理解泛型的人。为了让大家能够更好的理解泛型,故开此贴,欢迎大家积极发表自己的意见(btw:如果你只会在这个帖子里回“学习”两字,但其实什么都没学到的话,那我还是奉劝你还是不要回了,还不如到别的散分贴接点分来得实在)


首先来说说什么是泛型,泛型是.net framework2.0版本以后才出现的新东西,泛型的英文为Generic,意为“通用”,翻译成为泛型之后更能体现其优点:广泛的类型。设想有这么一个情况,你正在设计一个自定义类型,这个类里面的很多方法都要传进一个参数,但这个参数的类型在设计阶段还不能知道它是什么类型的,那怎么办呢?在泛型出来之前,我们只能用所有类型的object类型来进行定义,但是用object会有很多问题,比如你的参数传进来以后你要把它强制转换类型,这就涉及到代码的隐患了,另外还有就是如果是值类型的话,还会有装箱和拆箱的各种性能损耗。为了解决这些问题,于是泛型出现了,在定义的时候,你只要将不确定的类定义为泛型类型,在使用的时候,根据需要用特定的类型来代替泛型类型,这就保证了类型的安全性,并且减少了装拆箱的性能损耗。所以泛型最普遍的就是用来定义泛型集合类。

以下为MSDN对泛型的概述:
泛型概述
使用泛型类型可以最大限度地重用代码、保护类型的安全以及提高性能。

泛型最常见的用途是创建集合类。

.NET Framework 类库在 System.Collections.Generic 命名空间中包含几个新的泛型集合类。应尽可能地使用这些类来代替普通的类,如 System.Collections 命名空间中的 ArrayList。

您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。

可以对泛型类进行约束以访问特定数据类型的方法。

关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。


一般情况下,创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡。创建您自己的泛型类时,需要特别注意以下事项:

1、将哪些类型通用化为类型参数。

通常,能够参数化的类型越多,代码就会变得越灵活,重用性就越好。但是,太多的通用化会使其他开发人员难以阅读或理解代码。

2、如果存在约束,应对类型参数应用什么约束

一条有用的规则是,应用尽可能最多的约束,但仍使您能够处理必须处理的类型。例如,如果您知道您的泛型类仅用于引用类型,则应用类约束。这可以防止您的类被意外地用于值类型,并允许您对 T 使用 as 运算符以及检查空值。

3、是否将泛型行为分解为基类和子类。

由于泛型类可以作为基类使用,此处适用的设计注意事项与非泛型类相同。请参见本主题后面有关从泛型基类继承的规则。

4、是否实现一个或多个泛型接口。

例如,如果您设计一个类,该类将用于创建基于泛型的集合中的项,则可能必须实现一个接口,如 IComparable<T>,其中 T 是您的类的类型。


C# 泛型和 C++ 模板都是用于提供参数化类型支持的语言功能。然而,这两者之间存在许多差异。在语法层面上,C# 泛型是实现参数化类型的更简单方法,不具有 C++ 模板的复杂性。此外,C# 并不尝试提供 C++ 模板所提供的所有功能。在实现层面,主要区别在于,C# 泛型类型替换是在运行时执行的,从而为实例化的对象保留了泛型类型信息。

以下是 C# 泛型和 C++ 模板之间的主要差异:

C# 泛型未提供与 C++ 模板相同程度的灵活性。例如,尽管在 C# 泛型类中可以调用用户定义的运算符,但不能调用算术运算符。

C# 不允许非类型模板参数,如 template C<int i> {}。

C# 不支持显式专用化,即特定类型的模板的自定义实现。

C# 不支持部分专用化:类型参数子集的自定义实现。

C# 不允许将类型参数用作泛型类型的基类。

C# 不允许类型参数具有默认类型。

在 C# 中,尽管构造类型可用作泛型,但泛型类型参数自身不能是泛型。C++ 确实允许模板参数。

C++ 允许那些可能并非对模板中的所有类型参数都有效的代码,然后将检查该代码中是否有用作类型参数的特定类型。C# 要求相应地编写类中的代码,使之能够使用任何满足约束的类型。例如,可以在 C++ 中编写对类型参数的对象使用算术运算符 + 和 - 的函数,这会在使用不支持这些运算符的类型来实例化模板时产生错误。C# 不允许这样;唯一允许的语言构造是那些可从约束推导出来的构造。


------------------------------------------------------------------------
应8楼的要求,列几个例子说明一下:
下面的代码示例演示一个用于演示用途的简单泛型链接列表类。(大多数情况下,应使用 .NET Framework 类库提供的 List<T> 类,而不是自行创建类。)在通常使用具体类型来指示列表中存储的项的类型的场合,可使用类型参数 T。其使用方法如下:

在 AddHead 方法中作为方法参数的类型。

在 Node 嵌套类中作为公共方法 GetNext 和 Data 属性的返回类型。

在嵌套类中作为私有成员数据的类型。

注意,T 可用于 Node 嵌套类。如果使用具体类型实例化 GenericList<T>(例如,作为 GenericList<int>),则所有的 T 都将被替换为 int。



// 在三角符号里写入类型参数T
public class GenericList<T>
{
// Node为非泛型类,作为GenericList<T>的嵌套类
private class Node
{
// 在非泛型构造函数中使用T
public Node(T t)
{
next = null;
data = t;
}

private Node next;
public Node Next
{
get { return next; }
set { next = value; }
}

// T作为私有成员的数据类型
private T data;

// T作为属性的返回类型
public T Data
{
get { return data; }
set { data = value; }
}
}

private Node head;

// 构造函数
public GenericList()
{
head = null;
}

// T 作为方法的参数类型
public void AddHead(T t)
{
Node n = new Node(t);
n.Next = head;
head = n;
}

public IEnumerator<T> GetEnumerator()
{
Node current = head;

while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
}



下面的代码示例演示客户端代码如何使用泛型 GenericList<T> 类来创建整数列表。只需更改类型参数,即可方便地修改下面的代码示例,创建字符串或任何其他自定义类型的列表:


class TestGenericList
{
static void Main()
{
// int 是类型变量
GenericList<int> list = new GenericList<int>();

for (int x = 0; x < 10; x++)
{
list.AddHead(x);
}

foreach (int i in list)
{
System.Console.Write(i + " ");
}
System.Console.WriteLine("\n完成");
}
}

...全文
4090 195 打赏 收藏 转发到动态 举报
写回复
用AI写文章
195 条回复
切换为时间正序
请发表友善的回复…
发表回复
FlyBird2004 2012-05-07
  • 打赏
  • 举报
回复
在应用 where T : class 约束时,避免对类型参数使用 == 和 != 运算符,因为这些运算符仅测试引用同一性而不测试值相等性。即使在用作参数的类型中重载这些运算符也是如此。下面的代码说明了这一点;即使 String 类重载 == 运算符,输出也为 false。

怎么可能?约束难道不是在实例化的时候启的作用吗?

public class AA { public string IdStr;}
public class BB : AA { string Name;}
public class CC : BB
{
public string Desc;

public Boolean compareId<T>( T obj1, T obj2) where T : AA
{
return obj1.IdStr == obj2.IdStr;
}
}


class Program
{
static void Main(string[] args)
{
CC Nc = new CC();
AA Na = new AA();
Nc.IdStr = "Hello world!";
Na.IdStr = "Hello world!";

Console.WriteLine(Nc.compareId(Nc, Na).ToString());
}
}
yangchun1213 2012-04-27
  • 打赏
  • 举报
回复
例子没看明白
gudicao 2012-02-19
  • 打赏
  • 举报
回复
懂了,转了
yojinlin 2012-02-19
  • 打赏
  • 举报
回复
學習了。
larissa523 2012-02-19
  • 打赏
  • 举报
回复
学习了。
liuyuainil 2012-02-17
  • 打赏
  • 举报
回复
学习了,但楼住的内容是摘抄的
gudicao 2012-02-17
  • 打赏
  • 举报
回复
收获不大,但是有收获
wdmqepwq 2011-12-22
  • 打赏
  • 举报
回复
正在学泛型。。还不是很懂
gdmvip 2011-10-25
  • 打赏
  • 举报
回复
希望楼主给一个实在的例子,要简单的,我刚刚学这个泛型,据说很好用很方便,但是学了2天网上找资料也没学好。能给几个简单的例子吗?在开发中的例子。
wanbolantian 2011-10-24
  • 打赏
  • 举报
回复
[Quote=引用 60 楼 xfreyes 的回复:]

引用 50 楼 mlliqiushi 的回复:
泛型的应用主要在哪呢???
没看太明白!~~


我也不明白,没怎么用过,可不可以麻烦举个例子说明一下,分别是使用泛型和不使用泛型情况,比较一下说明使用泛型的好处。
谢谢
[/Quote]
同求,什么情况下该用泛型?感觉实际的开发中,并没有多少机会碰到使用泛型?
bjwnaxin 2011-09-14
  • 打赏
  • 举报
回复
多谢,但是还是感觉内容太少了
松林迷途 2010-08-30
  • 打赏
  • 举报
回复
不错,好好学习之!
xk029 2010-07-21
  • 打赏
  • 举报
回复
li329346351 2010-07-19
  • 打赏
  • 举报
回复
学习ing·····
Mandoli 2010-07-15
  • 打赏
  • 举报
回复
感觉是专门处理集合类的万能螺丝刀
SuperTar 2010-06-19
  • 打赏
  • 举报
回复

逻辑混乱
杯具
zy2027396 2010-06-18
  • 打赏
  • 举报
回复
mark
mansheng 2010-06-05
  • 打赏
  • 举报
回复
现在正好要用到泛型,找个时间好好欣赏一番
aresnet 2010-06-04
  • 打赏
  • 举报
回复
还可以再问问题吗?
泛型的用途,最关键的就是在设计阶段不用考虑传入的是什么具体的类型,只用一个泛型类型来代替,只有在具体使用的时候才将具体类型代替泛型类型
可以举个例子吗?
wyx382978297 2010-05-21
  • 打赏
  • 举报
回复
我也仔细看了一下;可能是我太菜了,还是不怎么清楚;
我现在想做一个操作数据库的通用类;

//这个方法把传来的实体类信息插入到数据库,但只能传一个实体
public void AddAll<T>(T Entity)
{
把实体信息传过来存到数据库,可以是任何实体;
比如可以传一个老师类或者一个学生类;
}

我现在想实现差不多实体数组
想写一个方法实现Add() 这个方法可以传几个学生,或几个老师信息;

想过List<Stu> lstu =new List<Stu>();
stu s=new stu();//这个是学生实体类
s.uid="001";
s.uname="张三";
lstu.add(s);//添加第一个学生

s.uid="002";
s.uname="李四";
lstu.add(s);//添加第二个学生

但是怎么传过去呢;写一个方法SQL.add()这个括号里我该怎样定义参数类型才能接收lstu呢;
还有,这个lstu怎么遍历呢.

不知道我有没有说清楚
加载更多回复(162)

110,545

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧