1 泛型
泛型核心作用:可以让多个类型共享一组代码
C#中提供了5种泛型:类、结构、接口、委托和方法。前面四个是类型,而方法是成员。
类型不是对象而是对象的模板,泛型类不是类型,而是类型的模板。
2 声明泛型类
在类名之后放置一组尖括号;
在尖括号中用逗号分割的占位符(通常是T,也可以是其他的)来表示类型参数;
在泛型类声明的主体中使用类型参数来替代实际的类型。
class SomeClass<T1, T2>
{
public T1 someVar;
public T2 otherVar ;
}
3 创建构造类型
列出类名并在尖括号内传入类型实参
SomeClass<short, int> myScl = new SomeClass<short, int>();
这样以后,编译器就使用类型实参来产生一个构造类型,这里就是产生了一个构造类
class SomeClass<short, int>
{
short someVar;
int otherVar ;
}
4 创建变量和实例
5 泛型栈
class MyStack<T>
{
const int MaxStack = 10;
T[] StackArray;
int StackPointer = 0;
bool IsStackFull { get { return StackPointer >= MaxStack; } }
bool IsStackEmpty { get { return StackPointer <= 0; } }
public void Push(T x)
{
if (!IsStackFull)
{
StackArray[StackPointer++] = x;
}
}
public T Pop()
{
return (!IsStackEmpty) ? StackArray[--StackPointer] : StackArray[0];
}
public MyStack()
{
StackArray = new T[MaxStack];
}
public void Print()
{
for (int i = StackPointer - 1; i >= 0; i--)
Console.WriteLine(" Value:{0}", StackArray[i]);
}
}
class Program2
{
static void Main()
{
MyStack<int> StackInt = new MyStack<int>();
MyStack<string> StackString = new MyStack<string>();
StackInt.Push(3);
StackInt.Push(5);
StackInt.Push(7);
StackInt.Push(9);
StackInt.Print();
StackString.Push("This is fun");
StackString.Push("Hi there!");
StackString.Print();
}
}
输出结果:
6 类型参数的约束 Where
class MyClass<T1, T2, T3>
where T2 : Customer //对于T2必须是:Customer类型或从Customer继承的类型
where T3 : IComparable//对于T3必须是:实现IComparable接口的类
{
}
可以有5种类型约束:(where约束冒号后面)
类名: 表示只有这个类型的类或者从它继承的类才能用作类型实参
class: 表示任何引用类型,包括类、数组、委托和接口都可以用作类型实参
struct: 任何值类型都可以用作类型实参
接口名: 只有这个接口或者实现这个接口的类型才能用作类型实参
new(): 表示任何带有无参公共构造函数的类型都可以用作该类型实参,这叫做构造函数约束
一个类型可以有多种约束,但是要有顺序,最多只能有一个主约束,且要放在第一位,可以有任意多的接口约束,如果存在构造函数约束,必须放在最后。
class MyClass<T1, T2, T3>
where T2 : Customer
where T3 : IComparable,new()
{
}
7 泛型方法
泛型方法声明:
public void PrintData<S, T>(S s, T t) where S : Person
{ ……}
<S, T>为类型参数列表 (S s, T t)才是方法参数
以下是一个泛型方法的例子:
classSimple
{
static public void ReverseAndPrint<T>(T[] arr) //这里声明了一个泛型方法,这个泛型方法可以任意类型的数组作为其参数类型
{
Array.Reverse(arr);
foreach (T item in arr)
Console.Write("{0}", item.ToString());
Console.WriteLine("");
}
}
class Program2
{
static void Main()
{
var intArray = new int[] { 3, 5, 7, 9, 11 };
var stringArray = new string[] { "first", "second", "third" };
var doubleArray = new Double[] { 3.1, 2.34, 5.5555 };
Simple.ReverseAndPrint<int>(intArray);//使用显示类型参数调用
Simple.ReverseAndPrint(intArray);//编译器可以从方法参数中推断出类型参数上面调用时在ReverseAndPrint中对该数组的反转在这里结果是保留的因为传入的是值类型
Simple.ReverseAndPrint<string>(stringArray);
Simple.ReverseAndPrint(stringArray);
Simple.ReverseAndPrint<double>(doubleArray);
Simple.ReverseAndPrint(doubleArray);
}
}
结果如下:
8 泛型类的扩展方法
泛型类的扩展方法:扩展方法必须声明为static;必须是静态类的成员;第一个参数前面必须有关键字this,后面是所扩展的泛型类的名字。
泛型类的扩展方法允许我们将类中的静态方法关联到不同的泛型类上。
示例代码:
static class ExtendHolder
{
public static void Print<T>(thisHolder<T> h) //扩展方法三要素:static类 static方法 this
//这里把this后面的所扩展的类使用了泛型类
{
T[] vals = h.GetValues();
Console.WriteLine("{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 Program2
{
staticvoid Main()
{
var intHolder = new Holder<int>(3, 5, 7);
var stringHolder = new Holder<string>("a3", "b3", "c3");
intHolder.Print(); // 这句其实等价于 ExtendHolder.Print<int>(intHolder);
stringHolder.Print();
}
}
代码结果:
9 泛型结构
struct PieceOfData<T> //声明了一个泛型结构
{
private T _data;
public T Data
{
get { return _data; }
set { _data = value; }
}
public PieceOfData(T valus)//这叫构造类型 类似构造函数
{
_data = valus;
}
}
classProgram2
{
staticvoid Main()
{
var intData = new PieceOfData<int>(10);
var strimgData = new PieceOfData<string>("Hi");
Console.WriteLine("intDate={0}", intData.Data);
Console.WriteLine("stringData={0}", strimgData.Data);
}
}
泛型结构也可以由类型参数和约束,和泛型类一样。
10 泛型委托
泛型委托的声明:
delegate R MyDelegate<T,R>(T value);
R是返回类型 <>里面是类型参数 T是委托参数
泛型委托代码示例:
delegate void MyDelegate<T>(T value); //声明一个泛型委托
class Simple
{
static public void PrintString(string s) //这个类定义了两个静态方法
{
Console.WriteLine(s);
}
static public void PrintUpperString(string s)
{
Console.WriteLine("{0}", s.ToUpper());
}
}
class Program
{
staticvoid Main(string[] args)
{
var myDel = new MyDelegate<string>(Simple.PrintString);//创建委托实例
myDel += Simple.PrintUpperString;//添加方法
myDel("Hi");//调用委托
}
}
创建委托实例时,使用string类型来对泛型的类型参数进行参数实例化,而后面的静态方法PrintString对委托进行初始化。这个方法是和委托是匹配的,即泛型如果是string,则方法传入参数必须是string。
11 泛型接口
示例代码:
(1) 泛型类实现泛型接口
interfaceIMyIf<T> //声明泛型接口
{
T ReturnIt(T inValue);
}
classSimple<S> : IMyIf<S>//使用泛型类Simple来实现了这个泛型接口
{
public S ReturnIt(S inValue)
{ return inValue; }
}
classProgram
{
staticvoid Main(string[] args)
{
var trivInt = newSimple<int>();//实例化泛型类对象trivInt trivString
var trivString = newSimple<string>();
Console.WriteLine(trivInt.ReturnIt(3));
Console.WriteLine(trivString.ReturnIt("Hi"));
Console.ReadLine();
}
}
(2)非泛型类实现两个泛型接口
上面的代码是使用泛型类来实现的泛型接口,我们也可以在非泛型类中实现泛型接口,如下代码:
interfaceIMyIfc<T> //声明泛型接口
{
T ReturnIt(T inValue);
}
classSimple : IMyIfc<int>, IMyIfc<string> //非泛型类实现了两个接口
{ //这是源于同一泛型类的两个不同的接口
publicint ReturnIt(int inValue)
{ return inValue; }
publicstring ReturnIt(string inValue)
{ return inValue; }
}
classProgram
{
staticvoid Main(string[] args)
{
Simple trivial = newSimple();
Console.WriteLine(trivial.ReturnIt(5));
Console.WriteLine(trivial.ReturnIt("Hi"));
}
}
这里类型参数不一样的接口,事实上就是不同的接口。
(3) 泛型接口的实现必须唯一
interfaceIMyIfc<T> //声明泛型接口
{
T ReturnIt(T inValue);
}
classSimple<S> : IMyIfc<int>, IMyIfc<S> //这里错误,因为参数类型如果是int的话
{ //Simple是有两个相同类型的接口,这是不允许的
publicint ReturnIt(int inValue)
{ return inValue; }
public S ReturnIt(S inValue)
{ return inValue; }
}
classProgram
{
staticvoid Main(string[] args)
{
var trivialInt = newSimple<int>();
var trivalString = newSimple<string>();
Console.WriteLine(trivialInt.ReturnIt(5));
Console.WriteLine(trivalString.ReturnIt("Hi"));
Console.ReadLine();
}
}
12 协变
classAnimal
{
publicint NumsOfLegs = 4;
}
classDog : Animal
{ }
classProgram
{
staticvoid Main(string[] args)
{
Animal a1 = newAnimal();
Animal a2 = newDog();
Console.WriteLine(a2.NumsOfLegs);
}
}
赋值兼容性:可以将派生类对象的实例赋值给基类的变量。
实例代码:
classAnimal
{
publicint NumsOfLegs = 4;
}
classDog : Animal
{ }
delegate T Factory< T>();//声明一个泛型委托
classProgram
{
staticDog MakeDog()//符合Factory委托的匹配方法
{
returnnewDog();
}
staticvoid Main(string[] args)
{
Factory<Dog> dogMaker = MakeDog; //创建委托对象
Factory<Animal> animalMaker = dogMaker;//尝试赋值给Factory<Animal>类型委托 报错
Console.WriteLine(animalMaker().NumsOfLegs);
}
}
以上代码会报错,从代码返回值看, Factory<Animal> animalMaker返回的是Animal类型对象引用,而Factory<Dog> dogMaker返回的是Dog类型引用,根据赋值兼容性是可以的,但是,因为委托Factory<Dog>没有从委托Factory<Animal>中派生,是两个独立等级别的委托,所以赋值兼容性不适用。
解决办法:协变
delegate T Factory<out T>();
用out关键字指定了类型参数的协变
13 逆变
示例代码:
classAnimal { publicint NumbersOfLegs = 4;}
classDog : Animal { }
classProgram
{
delegatevoidAction1<in T>(T a);
staticvoid ActOnAnimal(Animal a) { Console.WriteLine(a.NumbersOfLegs); }
staticvoid Main(string[] args)
{
Action1<Animal> act1 = ActOnAnimal;
Action1<Dog> dog1 = act1;
dog1(newDog());
Console.ReadLine();
}
}
逆变:在期望传入基类时,允许传入派生对象的特性叫做逆变。