当前位置:  编程技术>.net/c#/asp.net

C#泛型全面讲解

    来源: 互联网  发布时间:2014-08-30

    本文导语:  先来看一段C#泛型的代码,它的功能是实现一个栈,这个栈只能处理int数据类型:   public class Stack { private int[] m_item; public int Pop(){...} public void Push(int item){...} public Stack(int i) { this.m_item = new int[i]; } }   当我们需要一个栈...

先来看一段C#泛型的代码,它的功能是实现一个栈,这个栈只能处理int数据类型:
 

public class Stack
{
private int[] m_item;
public int Pop(){...}
public void Push(int item){...}
public Stack(int i)
{
this.m_item = new int[i];
}
}
 

当我们需要一个栈来保存string类型时,该怎么办呢?很多人都会想到把上面的代码复制一份,把int改成 string不就行了。当然,这样做本身是没有任何问题的,但一个优秀的程序是不会这样做的,因为他想到若以后再需要long、Node类型的栈该怎样做 呢?还要再复制吗?优秀的程序员会想到用一个通用的数据类型object来实现这个栈:
 

public class Stack
{
private object[] m_item;
public object Pop(){...}
public void Push(object item){...}
public Stack(int i)
{
this.m_item = new[i];
}
}
 

这个栈写的不错,他非常灵活,可以接收任何数据类型,可以说是一劳永逸。但全面地讲,也不是没有缺陷的,主要表现在:
当Stack处理值类型时,会出现装箱、折箱操作,这将在托管堆上分配和回收大量的变量,若数据量大,则性能损失非常严重。 在处理引用类型时,虽然没有装箱和折箱操作,但将用到数据类型的强制转换操作,增加处理器的负担。 在数据类型的强制转换上还有更严重的问题(假设stack是Stack的一个实例): Node1 x = new Node1();
 

stack.Push(x);
Node2 y = (Node2)stack.Pop();


上面的代码在编译时是完全没问题的,但由于Push了一个Node1类型的数据,但在Pop时却要求转换为Node2类型,这将出现程序运行时的类型转换异常,但却逃离了编译器的检查。

针对object类型栈的问题,我们引入泛型,他可以优雅地解决这些问题。泛型用用一个通过的数据类型T来代替object,在类实例化时指定T的类型,运行时(Runtime)自动编译为本地代码,运行效率和代码质量都有很大提高,并且保证数据类型安全。

使用泛型 下面是用泛型来重写上面的栈,用一个通用的数据类型T来作为一个占位符,等待在实例化时用一个实际的类型来代替。让我们来看看泛型的威力:
 

public class Stack
{
private T[] m_item;
public T Pop(){...}
public void Push(T item){...}
public Stack(int i)
{
this.m_item = new T[i];
}
}

类的写法不变,只是引入了通用数据类型T就可以适用于任何数据类型,并且类型安全的。这个类的调用方法:
 

//实例化只能保存int类型的类
Stack a = new Stack(100);
a.Push(10);
a.Push("8888"); //这一行编译不通过,因为类a只接收int类型的数据
int x = a.Pop();

//实例化只能保存string类型的类
Stack b = new Stack(100);
b.Push(10); //这一行编译不通过,因为类b只接收string类型的数据
b.Push("8888");
string y = b.Pop();

这个类和object实现的类有截然不同的区别:
1. 他是类型安全的。实例化了int类型的栈,就不能处理string类型的数据,其他数据类型也一样。
2. 无需装箱和折箱。这个类在实例化时,按照所传入的数据类型生成本地代码,本地代码数据类型已确定,所以无需装箱和折箱。
3. 无需类型转换。

泛型类实例化的理论 C#泛型类在编译时,先生成中间代码IL,通用类型T只是一个占位符。在实例化类时,根据用户指定的数据类型代替T并由 即时编译器(JIT)生成本地代码,这个本地代码中已经使用了实际的数据类型,等同于用实际类型写的类,所以不同的封闭类的本地代码是不一样的。按照这个 原理,我们可以这样认为:
泛型类的不同的封闭类是分别不同的数据类型。
例:Stack和Stack是两个完全没有任何关系的类,你可以把他看成类A和类B,这个解释对泛型类的静态成员的理解有很大帮助。

泛型类中数据类型的约束 程序员在编写泛型类时,总是会对通用数据类型T进行有意或无意地有假想,也就是说这个T一般来说是不能适应所有类 型,但怎样限制调用者传入的数据类型呢?这就需要对传入的数据类型进行约束,约束的方式是指定T的祖先,即继承的接口或类。因为C#的单根继承性,所以约 束可以有多个接口,但最多只能有一个类,并且类必须在接口之前。这时就用到了C#2.0的新增关键字:
 

public class Node where T : Stack, IComparable
where V: Stack
{...}
 

以上的泛型类的约束表明,T必须是从Stack和IComparable继承,V必须是Stack或从Stack继承,否则将无法通过编译器的类型检查,编译失败。

通用类型T没有特指,但因为C#中所有的类都是从object继承来,所以他在类Node的编写中只能调用object类的方法,这给程序的编写造 成了困难。比如你的类设计只需要支持两种数据类型int和string,并且在类中需要对T类型的变量比较大小,但这些却无法实现,因为object是没 有比较大小的方法的。 了解决这个问题,只需对T进行IComparable约束,这时在类Node里就可以对T的实例执行CompareTo方法了。这个问题可以扩展到其他用 户自定义的数据类型。

如果在类Node里需要对T重新进行实例化该怎么办呢?因为类Node中不知道类T到底有哪些构造函数。为了解决这个问题,需要用到new约束:
 

public class Node where T : Stack, new()
where V: IComparable


需要注意的是,new约束只能是无参数的,所以也要求相应的类Stack必须有一个无参构造函数,否则编译失败。
C#中数据类型有两大类:引用类型和值类型。引用类型如所有的类,值类型一般是语言的最基本类型,如int, long, struct等,在泛型的约束中,我们也可以大范围地限制类型T必须是引用类型或必须是值类型,分别对应的关键字是class和struct:
 

public class Node where T : class
where V: struct

泛型方法 泛型不仅能作用在类上,也可单独用在类的方法上,他可根据方法参数的类型自动适应各种参数,这样的方法叫泛型方法。看下面的类:
 

public class Stack2
{
public void Push(Stack s, params T[] p)
{
foreach (T t in p)
{
s.Push(t);
}
}
}

原来的类Stack一次只能Push一个数据,这个类Stack2扩展了Stack的功能(当然也可以直接写在Stack中),他可以一次把多个数据压入Stack中。其中Push是一个泛型方法,这个方法的调用示例如下:
 

Stack x = new Stack(100);
Stack2 x2 = new Stack2();
x2.Push(x, 1, 2, 3, 4, 6);
string s = "";
for (int i = 0; i < 5; i++)
{
s += x.Pop().ToString();
} //至此,s的值为64321

泛型中的静态成员变量 在C#1.x中,我们知道类的静态成员变量在不同的类实例间是共享的,并且他是通过类名访问的。C#2.0中由于引进了泛型,导致静态成员变量的机制出现了一些变化:静态成员变量在相同封闭类间共享,不同的封闭类间不共享。

 1 2 3 下一页 尾页

    
 
 
 
本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • -- 哪位大侠给我讲解、讲解这个口令啥意思? --
  • 100分求学“流”!流好难理解啊。谁能来讲解讲解吗?
  • 谁能给我讲解一下weblogic最基本的配置(希望你看在分的面子上,给我讲解讲解吧)
  • 关于尚德讲解的视频资料
  • 哪本linux源码分析的书里详细讲解了Linux的核心启动代码
  • 谁能给我详细讲解一下分区,文件系统的概念?
  • 有没有linux下讲解网络协议的好书?
  • 哪里有讲解J2EE和EJB的电子书下载?
  • 寻本详细讲解MAKEFILE和各编译器的书
  • 哪位能给讲解一下atomic_read()?
  • 谁来讲解一下linux下gtkmm的消息处理机制?[300分,讲得好的话,另加300分] 领分区01
  • 谁来讲解一下linux下gtkmm的消息处理机制?[300分,讲得好的话,另加300分]的领分区02
  • 100分求个详细讲解
  • NOSQL iis7站长之家
  • 哪位老大详细地讲解一下内核线程和轻量级进程、分别如何创建?多谢了。
  • 高手指点:听说jdk1.4能设置超时,请讲解一下?
  • ★★谁能详细的给我讲解synchronized的使用
  • 有没有比较经典的讲解makefile的教材/材料/电子书籍
  • 在线等!谁能给我讲解下这个图啊,linux tcp
  • 求大神详细讲解下KDbg的使用方法


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3