c#基础编程—泛型
一、引言
泛型的主要思想是将算法与数据结构完全分离开,使得一次定义的算法能作用于多种数据结构,从而实现高度可重用的开发。泛型,通过参数类型化来实现在同一份代码中操作多种数据类型,利用“参数化类型”将类型抽象化,从而实现更为灵活的运用。c#的泛型起源于c++的模板函数,当然在c#中进行升级。在c#中泛型实在编译时模板机制,c++是在运行时模板机制。其中泛型通过T来通知CLR在编译中利用特殊占位符表明此时在操作泛型操作,产生IL指令。
二、泛型的编译机制
在.net Framewrok 2.0 中,泛型在IL和CLR本身中具有本机支持。在编译泛型c#代码时,像其他任何类型一样,首先编译器会将其编译为IL,但是,IL只包含实际特定类型的参数或占位符,并有专用的IL指令支持泛型操作。泛型代码的元数据中包含泛型信息。真正的泛型实例化工作是以“on-demand”的方式,发生在JIT编译时。当进行JIT编译时,JIT编译器用指定的类型实参来替换泛型IL代码元数据中的T,进行泛型类型的实例化。
在第一轮编译时
第一轮编译时,编译器只为Stack(栈算法)类型产生“泛型版”的IL代码与元数据—–并不进行泛型类型的实例化,T在中间只充当占位符。JIT编译时,当JIT编译器第一次遇到Stack《T》时,将用int替换“泛型版”IL代码与元数据中的T—进行泛型类型的实例化。
CLR为所有类型参数为“引用类型”的泛型类型产生同一份代码;但如果类型参数为“值类型”,对每一个不同的“值类型”,CLR将为其产生一份独立的代码。
三、泛型约束
在泛型中应用最广的就是依据泛型约束进行拓展。C#的泛型采用“基类,接口,构造器,值类型/引用类型”的约束方式来实现对类型能数的“显式约束”,提高了类型安全的同时,也丧失了C++模板基于“签名”的隐式约束所具有的高灵活性。C#泛型要求对“所有泛型类型或泛型方法的类型参数”的任何假定,都要基于“显式的约束”,以维护C#所要求的类型安全。
在泛型应用中,要尽量显示制定约束条件,当没有显示约束时,讲默认泛型类型集成于Object。在进行泛型约束后,在对泛型类操作时可以直接操作,很是方便,有点继承的感觉。
例如:
where T : struct 类型必须是一种值类型(struct)
where T : class 类型必须是一种引用类型(class)
where T : new() 类型必须有一个无参数的构造器
where T : class_name 类型可以是class_name或者是它的一个子类
where T : interface_name 类型必须实现指定的接口
- 1、基类约束
- class Stack1
- {
- private string name;
- public Stack1()
- {
- }
- public void Func1()
- {
- }
- }
- class Statck2
- {
- public Statck2()
- { }
- public void Func2(){}
- }
- //显示告诉Statck3中T的泛型类型继承与Statck1,V来自Statck2
- class Statck3<T,V> where T:Stack1 where V:Statck2
- {
- public Statck3(T t,V v)
- {
- //此处实例化了,所以t只能调用statck1的方法
- t.Func1();
- v.Func2();
- }
- }
- class Statck4<T> where T :new()
- {
- T t;
- public Statck4( )
- {
- //此处没有指明T来源于哪,默认来源于object,所以不能调用func1了
- // t.Func1();
- //如果上面不显示new(),此处编译不通过
- t = new T();
- }
- }
基类约束有两个功能:
(1)它允许在泛型类中使用由约束指定的基类所定义的成员。例如,可以调用基类的方法或者使用基类的属性。如果没有基类约束,编译器就无法知道某个类型实参拥有哪些成员。通过提供基类约束,编译器将知道所有的类型实参都拥有由指定基类所定义的成员。
(2)确保类型实参支持指定的基类类型参数。这意味着对于任意给定的基类约束,类型实参必须要么是基类本身,要么是派生于该基类的类,如果试图使用没有继承指定基类的类型实参,就会导致编译错误。
- 2、接口约束:
接口约束用于指定某个类型参数必须应用的接口。接口的两个主要功能和基类约束完全一样。有点类似基类约束。
基本形式 where T:interface-name
interface-name是接口的名称,可以通过使用由逗号分割的列表来同时指定多个接口。如果某个约束同时包含基类和接口,则先指定基类列表,再指定接口列表。
- interface IA<T>
- {
- T Func1();
- }
- interface IB
- {
- void Func2();
- }
- interface IC<T>
- {
- T Func3();
- }
- class MyClass<T, V>
- where T : IA<T>
- where V : IB, IC<V>
- {
- public MyClass(T t, V v)
- {
- //T的对象可以调用Func1
- t.Func1();
- //V的对象可以调用Func2和Func3
- v.Func2();
- v.Func3();
- }
- }
- 3、值类型和引用类型约束:
如果引用类型和值类型之间的差别对于泛型代码非常重要,那么这些约束就非常有用。 基本形式: where T : class where T : struct 若同时存在其他约束的情况下,class或struct必须位于列表的开头。另外可以通过 使用约束来建立两个类型参数之间的关系 例如 class Generic Class2《T, V》 where V:T{} ——– 要求V必须继承于T,这种称为裸类型约束(naked type constraint)。
- //值类型和引用类型约束
- public struct A { }
- public class B { }
- public class C<T> where T : struct
- {
- }
- class D
- {
- public void Func()
- {
- C<A> c1 = new C<A>();
- //C<B> c2 = new C<B>();//error
- }
- }
四、泛型中的静态成员变量
在C#1.x中,我们知道类的静态成员变量在不同的类实例间是共享的,并且他是通过类名访问的。C#2.0中由于引进了泛型,导致静态成员变量的机制出现了一些变化:静态成员变量在相同封闭类间共享,不同的封闭类间不共享。
这也非常容易理解,因为不同的封闭类虽然有相同的类名称,但由于分别传入了不同的数据类型,他们是完全不同的类,比如:
Stack<\ a> = new Stack<\int>();
Stack<\int> b = new Stack<\int>();
Stack<\long> c = new Stack<\long>();
类实例a和b是同一类型,他们之间共享静态成员变量,但类实例c却是和a、b完全不同的类型,所以不能和a、b共享静态成员变量。
泛型中的静态构造函数
静态构造函数的规则:只能有一个,且不能有参数,他只能被.NET运行时自动调用,而不能人工调用。
泛型中的静态构造函数的原理和非泛型类是一样的,只需把泛型中的不同的封闭类理解为不同的类即可。以下两种情况可激发静态的构造函数:
1. 特定的封闭类第一次被实例化。
2. 特定封闭类中任一静态成员变量被调用。
五、综述
泛型使代码可以重用,类型和内部数据可以在不导致代码膨胀的情况下进行更改,而不管是值类型还是引用类型。可以一次性地开发、测试和部署代码,通过任何类型(包括将来的类型)来重用它,并且全部具有编译器支持和类型安全。因为泛型代码不会强行对值类型进行装箱和取消装箱,或者对引用类型进行向下强制类型转换,所以性能得到显著提高。对于值类型,性能通常会提高200%;对于引用类型,在访问该类型时,也可以达到预期性。
c#基础编程—泛型的更多相关文章
- [.net 面向对象编程基础] (18) 泛型
[.net 面向对象编程基础] (18) 泛型 上一节我们说到了两种数据类型数组和集合,数组是指包含同一类型的多个元素,集合是指.net中提供数据存储和检索的专用类. 数组使用前需要先指定大小,并且检 ...
- Winsock基础编程
Winsock基础编程 Socket的英文原义是"孔"或"插座".作为BSD UNIX的进程通信机制,取后一种意思.通常也称作"套接字",用 ...
- 黑马程序员:Java基础总结----泛型(高级)
黑马程序员:Java基础总结 泛型(高级) ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! 泛型(高级) 泛型是提供给javac编译器使用的,可以限定集合中的输入类型 ...
- 6、50道JAVA基础编程练习题跟答案
50道JAVA基础编程练习题 [程序1] 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 程序分析 ...
- 老李分享: 并行计算基础&编程模型与工具 1
老李分享: 并行计算基础&编程模型与工具 在当前计算机应用中,对高速并行计算的需求是广泛的,归纳起来,主要有三种类型的应用需求: 计算密集(Computer-Intensive)型应用,如 ...
- 【Socket】Java Socket基础编程
Socket是Java网络编程的基础,了解还是有好处的, 这篇文章主要讲解Socket的基础编程.Socket用在哪呢,主要用在进程间,网络间通信.本篇比较长,特别做了个目录: 一.Socket通信基 ...
- go基础编程 day-1
Go语言的特性 开启了学习新的语言路程,记录每天学习的笔记,与大家一起分享. ①.自动垃圾回收 ②.更丰富的内置类型 ③.函数多返回值 ④.错误处理 ⑤.匿名函数和闭包 ⑥.类型和接口 ⑦.并发编程 ...
- 简单的TSQL基础编程格式,存储过程,视图
这里简单整理一下数据库简单的编程,变量定义,赋值,分支语句和循环(这里以Sqlserver),以及存储过程格式 首先是变量定义,赋值,分支语句 --======TSQL数据库基础编程,定义变量,赋值, ...
- Hadoop 综合揭秘——MapReduce 基础编程(介绍 Combine、Partitioner、WritableComparable、WritableComparator 使用方式)
前言 本文主要介绍 MapReduce 的原理及开发,讲解如何利用 Combine.Partitioner.WritableComparator等组件对数据进行排序筛选聚合分组的功能.由于文章是针对开 ...
随机推荐
- Frameset布局
<FRAMESET> <FRAME> <NOFRAMES> <IFRAME> ■ 框架概念 : 所谓框架便是网页画面分成几个框窗,同时取得多个 URL. ...
- BitMap(比特位)
所谓的Bit-map就是用一个bit位来标记某个元素对应的Value, 而Key即是该元素.由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省. 腾讯面试的时候,让写了一个BitMap ...
- Android 学习手札(三) 视图(View)
在Android 系统红,任何可视化组件都需要从android.view.View类继承.可以使用两种方式创建View对象. · 一种方式是使用XML来配置View的相关属性,然后使用相应的方法来装载 ...
- Android Capability 细粒度的权限控制
1. 传统的UID/GID,权限颗粒度太大 2. Capability: 细粒度的权限控制 3. 进程的Capability 4. 文件的Capability 5. 进程的Capability Bou ...
- 设置(TableViewController)通用框架
本文学习于传播播客.李明杰老师.感谢
- Tomcat启动分析(Tomcat7.0)
1)bin目录下的bootstrap.jar中的main方法启动Tomcat org.apache.catalina.startup.Bootstrap类下的main方法 可以看到Bootstrap类 ...
- swift中类似宏定义
建一个类 如,在Contans.swift中 import UIKit let kMAIN_SIZE = UIScreen.mainScreen().bounds 在其他地方直接用 比如在 MyTab ...
- Asterix and Obelix
uva10246:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&am ...
- SparkSQL配置和使用初探
1.环境 OS:Red Hat Enterprise Linux Server release 6.4 (Santiago) Hadoop:Hadoop 2.4.1 Hive:0.11.0 JDK:1 ...
- WITH AS and materialize hints
WITH AS: 就是将一个子查询部分独立出来,有时候是为了提高SQL语句的可读性,有时候是为了提高SQL语句性能. 如果一个SQL语句中,某个表会被访问多次,而且每次访问的限制条件一样的话,就可以使 ...