C#泛型详解
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
C#泛型详解
⼀、泛型的定义及作⽤
泛型(generic)是C# 2.0推出的新语法,它是专门为处理多段代码在不同的数据类型上执⾏相同的指令的情况⽽设计的。
⽐如说编程时,碰到功能⾮常相似的模块,只是它们所处理的数据类型不同,然⽽我们却需要写不同的⽅法来实现它,很明显,这加⼤了我们的⼯作量,也很乏味。
有没有什么办法能够解决这个问题呢?它就是泛型了,可以让多个类型共享⼀组代码。
通过压栈例⼦可以更清楚的了解泛型
class IntStack
{int[] arr;
public void push(int x) { ...}; //将int类型的值压栈
}
class FloStack
{float[] arr;
public void push(float x) { ...};//将float类型的值压栈
}
这两个类功能⼀样,只是操作的数据类型不同,并且如果需要新类型(double、string)等时,⼜需要进⾏重复的操作,下⾯介绍怎么通过泛型解决这个问题
⼆、泛型的使⽤
class MyStack <T>
{
T[] arr;
public void push(T x) { ...};
}
创建泛型类时,先在类名后⾯添加<T>,并将类型占位符T替代int或float。
由尖括号和T构成的字符串表明T是类型的占位符(不⼀定是字母T,也可以是其他标识符)。
泛型类型不是类型,⽽是类型的模板,就好⽐类型不是对象⽽是对象的模板⼀样。
C#提供了五种泛型:类、结构、接⼝、委托和⽅法。
前⾯四个是类型,⽽⽅法是成员。
还是很迷吧,接下来对五种泛型分别进⾏讲解。
1、泛型类
由于泛型类不是实际的类,⽽是类的模板,所以我们必须先从它们构造实际的类类型,然后创建这个构造后的类类型的实例。
从泛型类型创建实例的过程是:a:声明泛型类型 b:通过提供真实类型创建构造类型 c: 从构造类型创建实例 下⾯的例⼦有助于理解
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace generic
{
class Test<T1, T2>//构造泛型类,T1,T2为类型参数
{
public T1 var1;
public T2 var2;
public void print()
{
Console.WriteLine("var1:{0} var2:{1}", var1 , var2);
}
}
class Program
{
static void Main(string[] args)
{
//两种实例化的⽅式,效果相同
Test<int,string> first = new Test<int, string>();//int和string为类型实参,分别对应T1,T2
var second = new Test<string, int>();
first.var1 = 123; //first中,var1只能为int类型,当然也可以通过强制类型转换成其他的类型了
first.var2 = "Good Luck!"; //只能为string类型,同上
first.print();
second.var1 = "hello world";
second.var2 = 345;
second.print();
Console.ReadKey();
}
}
}
同⼀个泛型可以构建出很多不同的类型,互不⼲扰,每⼀个都有独⽴的类类型,就好像有独⽴的⾮泛型类声明⼀样
2、泛型结构
struct MyStruct<T>
{
public T val;
}
和泛型类类似,不过多叙述
3、泛型委托
泛型委托和⾮泛型委托⾮常相似,不过类型参数决定了它能接受什么⽅法
delegate S MyDel <T, S>(T value);
//S为委托返回类型,T为委托参数类型
4、泛型接⼝
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace generic
{
interface Test<T>
{
void Print(T value);
}
/*
* 泛型的接⼝实现必须唯⼀,必须保证类型参数组合不会再类型中产⽣两个重复的接⼝
* class Simple<S> : Test<int>, Test<S> 是错误的,因为S有可能是int类型
* 你也可以再⾮泛型类型中实现泛型接⼝
* class Simple : Test<int>, Test<string>
*/
class Simple<S> : Test<S>
{
public void Print(S value)
{
Console.WriteLine("value is {0}",value);
}
}
class Program
{
static void Main(string[] args)
{
var IntSimp = new Simple<int>();
var StrSimp = new Simple<string>();
IntSimp.Print(123);
StrSimp.Print("hello world");
Console.ReadKey();
}
}
}
注:泛型接⼝的名字不会和⾮泛型冲突,我们可以在前⾯的代码中声明⼀个Test的⾮泛型接⼝,程序能正常执⾏
5、泛型⽅法
泛型⽅法有两个参数列表,封闭在圆括号内的⽅法参数列表和封闭在尖括号内的类型参数列表,如下所⽰ public void Print<S,T> (S val1, T val2)
{
...
}
三、类型参数的约束(constraint)
顾名思义,即对类型参数进⾏约束,让编译器知道参数可以接受哪些类型,只有符合约束的类型才能替代给定的类型参数,来产⽣构造类型对此,可以使⽤where⼦句,其语法为: where TypeParam : constraint, constraint ...
⼀个where对应⼀个类型参数,如果类型参数有多个约束,则⽤逗号分隔
class Test<T,S>
where T : IComparable
where S : new()
{...}
//不理解没关系,下⾯会讲
//注意两个where之间没有任何符号分隔,可以以任何次序列出
对于约束,有五种约束类型
where⼦句的约束必须有特定的顺序
a: 最多只能有⼀个主约束,如果有则必须放在第⼀位,主约束可以为结构、类或基类名
b: 可以有任意多的接⼝名约束,其类型为接⼝名称
c: 如果存在构造函数约束,则必须放在最后⾯,构造函数约束即为 new()
四、协变和逆变
“协变”是指能够使⽤与原始指定的派⽣类型相⽐,派⽣程度更⼤的类型。
“逆变”则是指能够使⽤派⽣程度更⼩的类型。
可以显式使⽤out关键字指定类型参数的协变,⽤in关键字指定类型参数的逆变
interface IItf<out T>{...}
delegate void Test<in T>(T a);
显式变化使⽤in和out关键字只适合于委托和接⼝,不适⽤于类、结构和⽅法。