泛型(generic)特性提供了一种更优雅的方式,可以让多个类型共享一组代码。泛型允许我们声明类型参数化(type-parameterized)的代码,可以用不同的类型进行实例化。也就是说,我们可以用“类型占位符”来写代码,然后在创建类的实例时指明真实的类型
泛型类不是实际的类,而是类的模板,所以我们必须先从它们构建实际的类类型,然后创建这个构建后的类类型的实例
- 在某些类型上使用占位符来声明一个类
- 为占位符提供真实类型。这样就有了真实类的定义,填补了所有的“空缺”。该类型称为构造类型(constructedtype)
- 创建构造类型的实例
声明一个简单的泛型类和声明普通类差不多
区别如下:
- 在类名之后放置一组尖括号
- 在尖括号中用逗号分隔的占位符字符串来表示希望提供的类型。这叫做类型参数(typcparameter)
- 在泛型类声明的主体中使用类型参数来表示应该替代的类型
// 类型参数
class SomeClass < T1, T2 >
{
// T1、T2表示在当前位置使用类型
public T1 SomeVar = new T1();
public T2 OtherVar = new T2();
}
一旦创建了泛型类型,我们就需要告诉编译器能使用哪些真实类型来替代占位符(类型参数)。编译器获取这些真实类型并创建构造类型(用来创建真实类对象的模板)
创建构造类型的语法如下,包括列出类名并在尖括号中提供真实类型来替代类型参数。要替代类型参数的真实类型叫做类型实参(typeargument)
SomeClass < short, int >
类型参数和类型实参的区别(下图)
MyNonGenClass myNGC = new MyNonGenClass ();
// 构造类 构造类
SomeClass < short, int > mySc1 = new SomeClass < short, int >();
var mySc2 = new SomeClass < short, int >();
ps: 可以从同一个泛型类构建出很多不同的类类型。每一个都有独立的类类型,就好像它们都有独立的非泛型声明一样
非泛型栈和泛型栈之间的区别
- | 非泛型 | 泛 型 |
---|---|---|
源代码大小 | 更大:我们需要为每一种类型编写一个新的实现 | 更小:不管构造类型的数量有多少,我们只需要一个实现 |
可执行大小 | 无论每一个版本的栈是否会被使用,都会在编译的版本中出现 | 可执行文件中只会出现有构造类型的类型 |
写的难易度 | 易于书写,因为它更具体 | 比较难写,因为它更抽象 |
维护的难易度 | 更容易出问题,因为所有修改需要应用到每一个可用的类型上 | 易于维护,因为只需要修改一个地方 |
约束使用where字句列出
where 参数类型 : 约束1, 约束2, ...
where 子句的要点:
约束类型和次序
类名 | 只有这个类型的类或从它继承的类才能用作类型实参 |
---|---|
class | 任何引用类型,包括类、数组、委托和接口都可以用作类型实参 |
struct | 任何值类型都可以用作类型实参 |
接口名 | 只有这个接口或实现这个接口的类型才能用作类型实参 |
new() | 任何带有无参公共构造函数的类型都可以用作类型实参。这叫做构造函数约束 |
ps:where子句可以以任何次序列出。然而,where子句中的约束必须有特定的次序
与其他泛型不一样,方法是成员,不是类型。泛型方法可以在泛型和非泛型以及结构和接口中声明
声明泛型方法
泛型方法具有类型参数列表和可选的约束
eg:
public void PrintData <S, T>(string str, int t) where S:Person
{
...
}
调用泛型方法
要调用泛型方法,应该在方法调用时提供类型实参
MyMethod
泛型方法的示例
class Simple // 非泛型类
{
static public void ReverseAndPrint<T>(T[] arr) // 泛型方法
{
Array.Reverse(arr);
foreach (T iten in arr) // 使用类型参T
Console.krite("(0),", item.ToString());
Console.Mriteline(*);
}
}
class Program
{
static void Kain()
{
//创建各种类型的数糕
var intArray … new int[] {3, 5, 7, 9, 11 };
var stringArray - new string[] { "first", "second", "third" };
var doubleArray * nex double[]{ 3.567, 7.891, 2.345 };
Simple.ReverseAndPrintcint>(intArray); // 调用方法
Simple.ReverseAndPrint(intArray); // 推断类型并调用
Simple.ReverseAndPrint<string>(stringArray); // 调用方法
Simple.ReverseAndPrint(stringArray); // 推断类型并调用
Simple.ReverseAndPrintcdouble>(doubleArray); // 调用方法
Simple.ReverseAndPrint(doubleArray); // 推断类型并调用
}
}
上述案例的结果:
11, 9, 7, 5, 3
3, 5, 7, 9, 11
third, second, first
first, second, third
2.345, 7.891, 3.567
3.567, 7.891, 2.345
泛型类的扩展方法:
- 必须声明为static
- 必须是静态类的成员
- 第一个参数类型中必须有关键字this,后面是扩展的泛型类的名字
案例
static class ExtendHolder
{
public static void Print<T>(this Holder<T> h)
{
T[] vals= h.GetValues();
Console.NriteLine("{0},\t{1},\t{2}",vals[0], vals[1],vals[2]);
}
}
class Holder<T>
{
T[] Vals = new T[3];
public Holder(T V0, T V1,T v2)
{
vals[0] = v0;
Vals[1] = v1;
Vals[2] = v2;
}
public T[] GetValues()
{
return Vals;
}
class Program
{
static void Main(string[] args)
{
var intHolder = new Holder<int>(3, 5, 7);
var stringHolder = new Holder<string>("a1", "b2", "c3");
intHolder.Print();
stringHolder.Print();
}
}
上述案例的结果:
3, 5, 7
a1, b2, c3
案例
struct PieceOfData<T> //泛型结构
{
public PieceOfData(T value)
{
_data = value;
}
private T _data;
public T Data
{
set _data = value;
{
}
class Program
{
static void Main()
{
// 构造类型
var intData = new Piece0fData<int>(10);
// 构造类型
var stringData = new PieceOfData<string>("Hi there.");
Console.WriteLine("intData = {0}", intData.Data);
Console.WriteLine("stringData = {0}", stringData.Data);
}
}
案例结果:
intData = 10
stringData = Hi there
案例
delegate void MyDelegate<T>(T value); // 泛型委托
class Simple
{
static public void PrintString(string s) // 方法匹配委托
{
Console.WriteLine(s);
}
static public void PrintUpperString(string s) // 方法匹配委托
{
Console.MriteLine("(O)",s.ToUpper());
}
}
class Program
{
static void Main()
{
//创建委托的实例
var myDel = new MyDelegate<string>(Simple.PrintString);
//添加方法
myDel += Simple.PrintUpperString;
//调用委托
myDel("Hi There.");
}
}
案例结果:
Hi There
Hi There
泛型接口允许我们编写参数和接口成员返回类型是泛型类型参数的接口。泛型接口的声明和非泛型接口的声明差不多,但是需要在接口名称之后的尖括号中放置类型参数
案例
interface IMyIfc<T> // 泛型接口
{
T RetrnIt( T inValue);
}
// 类型参数 泛型接口
class Simple<S> :IMyIfc<S> // 泛型类
{
public S ReturnIt(S inValue) //实现泛型接口
{
return inValue
}
}
class Program
{
static void Main()
{
var trivInt = new Simple<int>();
var trivString = new Simple<string>();
Console.WriteLine("{0)",trivInt.ReturnIt(5));
Console.NriteLine("{o)",trivString.ReturnIt("Hi there."));
}
}
案例结果:
5
Hi there.
ps:泛型接口的实现必须唯一