ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
原文链接:https://blog.csdn.net/khqxf/article/details/88554247 有了泛型,可减少object对象的使用,对object进行装箱/拆箱时,性能不及泛型 1. 允许多种类型共享一组代码。在声明泛型时,给出类型参数,然后用不同类型创建实例。也就是在事先使用类型占位符,在实例化时才指出真实类型。 2. **泛型五种类型**:类、接口、结构、委托和方法。泛型是类型的模板,而类型是对象的模板。 3. 利用泛型实现栈的操作: ``` //泛型的基本用法 using System; namespace test28 { class Program { //利用泛型实现栈的操作 class Myclass<T> //声明泛型,T,S为类型占位符 { T[] StackArray; int StackPointer = 0; public void Push(T x) { if(!IsStackFull) StackArray[StackPointer++] = x; } public T Pop() { return (!IsStackEmpty)?StackArray[--StackPointer]:StackArray[0]; } const int MaxStack = 10; bool IsStackFull {get{return StackPointer >= MaxStack;}} bool IsStackEmpty {get{return StackPointer <= 0;}} public Myclass() { StackArray = new T[MaxStack]; } public void Print() { for(int i = StackPointer -1;i>=0;i--) { Console.WriteLine(" Info:{0}",StackArray[i]); } } } static void Main(string[] args) { Myclass<int> StackInt = new Myclass<int>(); Myclass<string> StackString = new Myclass<string>(); StackInt.Push(6); StackInt.Push(8); StackInt.Push(10); StackInt.Print(); StackString.Push("加油!2019!"); StackString.Push("自动化1401班!"); StackString.Print(); } } } ``` **类型参数的约束**:使用where子句对类型参数进行约束,where子句之间没有标点符号,如果某个类型参数有多个约束,在每一个where子句中用逗号间隔。`where TypeParams:constraint,constraint,...`示例如下 ``` class Myclass<T1,T2> where T1:Custom where T2:IComparable { ... } ``` 5 约束类型和次序 类名:只有这个类型的类或者从它继承的类才能用作类型实参 class:任何引用类型,包括类、数组、委托和接口都可以用作类型实参。 struct:任何值类型都可以用作类型实参。 接口名:只有这个接口或者实现这个接口的类型才可以用作类型实参。 new():任何带有无参公共构造函数的类型都可以用作类型实参,即为构造函数约束。 6 where子句的约束必须有一定的顺序,最多只能有一个主约束;可以有任意多的接口名约束;如果存在构造函数约束,则必须放在最后。 ``` class SortedList<S> where S:IComparable<S>{....} class LinkedList<M,N> where M:IComparable<M> where N:IComparable{....} class MyDictionary<KeyType,ValueType> where Keytype:IEnumerable, new() {...} ``` 7、泛型方法:它有两个参数列表,位于圆括号里的方法参数列表,位于尖括号里的类型参数列表。要声明泛型方法,要在方法名之后一次是类型参数列表和方法列表,在方法参数列表后是可选的约束子句 ``` public void PrintData<S,T>(S p,T t) where S:Person { ... } ``` 8 调用泛型方法: 在方法调用时提供类型实参,Mymethod<short,int>();Mymethod<int,long>();当然,这里也可用推断类型,倘若方法参数列表的类型已知,编辑器可以推断出类型参数列表的情况,这是就可以将类型参数列表给忽略,int myint = 5;Mymethod<int>(myint);可简写为Mymethod(myint); 泛型方法示例 ``` class Simple //非泛型类 { static public void ReverseAndPrint<T>(T[] arr) //泛型方法 { Array.Reverse(arr); foreach(T item in arr) { Console.WriteLine("{0},",item.ToString()); } Console.WriteLine(" "); } } class Program { static void Main() { //创建各种类型的数组 var intArray = new int[]{3,5,7,9}; var stringArray = new string[]{"first","second","third"}; var doubleArray = new double[]{3.456,4.567,5.678}; Simple.ReverseAndPrint<int>(intArray); //调用方法 Simple.ReverseAndPrint(intArray); //推断引用 Simple.ReverseAndPrint<string>(stringArray); //调用方法 Simple.ReverseAndPrint(intArray); //推断引用 Simple.ReverseAndPrint<double>(doubleArray); //调用方法 Simple.ReverseAndPrint(doubleArray); //推断引用 } } ``` 扩展方法和泛型类:将泛型类与扩展方法结合,允许我们将类中的静态方法关联到不同的泛型上,还可以像调用类结构实例那样调用方法。泛型类的扩展方法必须声明为static;必须是静态类的成员;第一个参数类型必须为this,后面是扩展的泛型类的名称,示例如下: ``` //扩展方法和泛型类 using System; namespace test30 { static class ExtendHoleder { public static void Print<T>(this Holder<T> h) { 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 t0,T t1,T t2) {vals[0]=t0;vals[1]=t1;vals[2]=t2;} 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(); } } } ``` 泛型结构:与泛型类相似,泛型结构可以有类型参数和约束。泛型结构的规则和条件与泛型类是一样的。示例如下 ``` //泛型结构 using System; namespace test31 { struct PieceOfData<T> { public PieceOfData(T value){_data = value;} private T _data; public T Data { get{return _data;} set{_data = value;} } } class Program { static void Main(string[] args) { var intData = new PieceOfData<int>(16); var stringData = new PieceOfData<string>("hi nihao!"); Console.WriteLine("intData ={0}",intData.Data); Console.WriteLine("stringData ={0}",stringData.Data); } } } ``` 泛型委托:泛型委托与非泛型委托相似,只是类型参数决定了能接受什么样的方法。要声明泛型委托,在委托名之后要有类型参数列表和委托参数列表;类型参数包括返回值、形参列表和约束子句。示例如下,泛型委托与泛型类。 ``` //泛型委托的简单示例 using System; namespace test32 { delegate T PrintT<T>(T t); //声明泛型委托,返回类型和参数列表类型都是T public class Myclass<T> //声明泛型类 { public T Method(T t) //方法的参数和返回类型与委托匹配 { return t; } } class Program { static void Main(string[] args) { Myclass<string> mycl = new Myclass<string>(); PrintT<string> prt = new PrintT<string>(mycl.Method); //等价于PrintT<string> prt = mycl.Method; 创建委托对象并赋值给委托变量 Console.WriteLine(prt("越努力越幸运,加油,自动化1401班!")); //执行委托 } } } ``` 泛型委托示例二,注意,此示例是泛型委托和非泛型类的使用。 ``` //泛型委托示例二 using System; namespace test33 { delegate T3 SumNum<T1,T2,T3>(T1 t1,T2 t2); //声明泛型委托,返回类型为T3,类型参数为T1,T2,T3,委托参数为T1,T2 public class Myclass //声明非泛型类 { private int _data; public string Method(int t1,int t2) //泛型方法的参数与返回类型与委托匹配 { _data = t1 + t2; return _data.ToString(); } } class Program { static void Main(string[] args) { Myclass mycl = new Myclass(); SumNum<int,int,string> sumn = new SumNum<int,int,string>(mycl.Method); //创建委托对象并赋值给委托变量 Console.WriteLine(sumn(4,5)); //执行委托 } } } ``` 泛型接口:泛型接口的类型参数决定了接口成员的参数类型和返回类型。在接口名后跟类型参数;泛型接口既可以在泛型类中实现也可以在非泛型类中实现;实现不同类型参数的泛型接口是不同的接口;泛型接口的实现必须唯一,不能重复实现相同的接口。 泛型接口示例: ``` //泛型接口示例一 using System; namespace test34 { class Program { public interface Multify<T> //声明泛型接口 {T Mulfy(T t);} public class Myclass<T>: Multify<T> //在泛型类中实现泛型接口 { public T Mulfy(T t) { return t; } } static void Main(string[] args) { Myclass<int> mycl = new Myclass<int>(); Console.WriteLine(mycl.Mulfy(16)); } } } ``` 代码过于简单,只是为了演示,结果是16 协变与逆变:协变允许将派生类的委托对象赋值给基类变量,逆变则与之相反;当然协变和逆变都可用于接口;协变的(委托或接口)类型参数必须声明为输出参数(关键字out),逆变的类型参数必须声明为输入参数(关键字in),在实现委托方法时,如果是协变,则方法的参数传入派生类的引用,实现派生类的方法;如果是逆变,传入基类的引用,实现基类的成员;协变的委托没有委托参数,逆变的委托有委托参数。 委托的协变和逆变示例: ``` using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace test35 { class Myclass //声明基类 { public string[] color = {"red","blue","yellow","orange","green"}; } class Myderived : Myclass {} //声明派生类 class Program { delegate void Mydele<out T>(); //声明泛型委托,协变时不能有委托参数,类型参数有out修饰 delegate void Mydele1<in T>(T t); //声明泛型委托,逆变时有委托参数,类型参数有in修饰 public static void Method() //协变,方法要与委托Mydele相匹配,在方法中实现派生类 { Myderived mydr = new Myderived(); Array.Reverse(mydr.color); //将数组倒序排列 foreach(string item in mydr.color) { Console.WriteLine(item); } } public static void Method1(Myclass mycl) //逆变,要与委托Mydele1相匹配,传入基类的引用,实现基类的成员 { foreach(string item in mycl.color) { Console.WriteLine(item); } } static void Main(string[] args) { Console.WriteLine("................协变................"); Mydele<Myderived> mydel = Method; //创建委托对象 Mydele<Myclass> mycl = mydel; //协变时,将派生类的委托对象赋值给基类的委托变量是可行的 mycl(); //执行委托 Console.WriteLine("................逆变................"); Mydele1<Myclass> mycll = Method1; //创建委托对象 Mydele1<Myderived> mydell = mycll; //逆变时,将基类的委托对象赋值给派生类的委托变量是可行的 mydell(new Myderived()); //执行委托,传入派生类的对象 } } } ``` 19. 接口的协变示例: ``` using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace test36 { //接口的协变 class Animal{public string Name;} //声明基类 class Dog:Animal{}; //声明派生类 interface IMyIfc<out T> //声明泛型接口 { T GetFirst(); } class SimpleReturn<T>:IMyIfc<T> //声明泛型类,实现泛型接口 { public T[] items = new T[2]; public T GetFirst(){return items[0];} } class Program { static void DoSomething(IMyIfc<Animal> returner) //传入接口的引用 { Console.WriteLine(returner.GetFirst().Name); } static void Main(string[] args) { SimpleReturn<Dog> dogReturner = new SimpleReturn<Dog>(); dogReturner.items[0] = new Dog(){Name = "WorkHarding"}; IMyIfc<Animal> animalReturner = dogReturner; DoSomething(dogReturner); } } } ``` 接口的逆变示例: ``` using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace test37 { //接口的逆变 class Mybase { public string[] str = {"red","orange","yellow","green","blue","violet"}; } class Myderived : Mybase{} interface IMyIfc<in T> //定义泛型接口,并且接口没有返回类型 { string GetFirst(); } class MyColor<T>:IMyIfc<T> //在泛型类中实现接口 { public string[] Color = new string[6] ; public string GetFirst() { Myderived mydr = new Myderived(); for(int i = 0;i<6;i++) { Color[i] = mydr.str[i]; } return Color[0]; } } class Program { public static void Choice(IMyIfc<Myderived> t) //传入接口的引用 { Console.WriteLine(t.GetFirst()); } static void Main(string[] args) { MyColor<Mybase> myco = new MyColor<Mybase>(); IMyIfc<Myderived> t = myco; Choice(myco); } } } ```