泛型编程简述
一、什么是泛型
泛型是通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用。
泛型的优点:1.可以做到最大的代码重用、保护类型的安全以及提高了程序运行的性能;
2.可以创建集合类;
3.可以创建自己的泛型接口、泛型方法、泛型类、泛型事件和泛型委托。
4.可以对泛型类进行约束,以访问特定数据类型的方法。
5.关于泛型数据类型中使用的类型的信息,可在运行时通过反射获取。
例子:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 int obj1 = 2; 6 var task = new Task<int>(obj1); 7 Console.WriteLine(task.obj); 8 9 string obj2 = "这是个泛型"; 10 var task2 = new Task<string>(obj2); 11 Console.WriteLine(task2.obj); 12 Console.ReadKey(); 13 14 } 15 } 16 17 class Task<T> 18 { 19 public T obj; 20 public Task(T t) 21 { 22 this.obj = t; 23 } 24 }
上述是通过定义泛型类,并通过在创建泛型类时,通过构造函数来改变字段的值;根据不同类型,得到不同类型的结果;
二、泛型约束
下表列出五种类型的约束:
约束 |
说明 |
| T:struct | 类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。 |
| T:class | 类型参数必须是引用类型,包括任何类、接口、委托或数组类型。 |
| T:new() | 类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。 |
| T:<基类名> | 类型参数必须是指定的基类或派生自指定的基类。 |
| T:<接口名称> | 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。 |
| T:U | 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束. |
派生约束
1.常见的
public class MyClass5<T> where T :IComparable { }
2.约束放在类的实际派生之后
public class B { }
public class MyClass6<T> : B where T : IComparable { }
3.可以继承一个基类和多个接口,且基类在接口前面
public class B { }
public class MyClass7<T> where T : B, IComparable, ICloneable { }
构造函数约束
1.常见的
public class MyClass8<T> where T : new() { }
2.可以将构造函数约束和派生约束组合起来,前提是构造函数约束出现在约束列表的最后
public class MyClass8<T> where T : IComparable, new() { }
等等一些约束,因本人理解未深入在此也无法一一简介;
三、泛型的协变与逆变
“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。
“逆变”则是指能够使用派生程度更小的类型。
直白的理解:
“协变”->”和谐的变”->”很自然的变化”->string->object :协变。
“逆变”->”逆常的变”->”不正常的变化”->object->string 逆变。
通过以下一段代码,来简单看下:
1 /// <summary> 2 /// 鸟 3 /// </summary> 4 public class Bird 5 { 6 public int Id { get; set; } 7 } 8 /// <summary> 9 /// 麻雀 10 /// </summary> 11 public class Sparrow : Bird 12 { 13 public string Name { get; set; } 14 }
我们分别取实例化这个类,发现程序是能编译通过的。
Bird bird1 = new Bird();
Bird bird2 = new Sparrow();
Sparrow sparrow1 = new Sparrow();
//Sparrow sparrow2 = new Bird();//这个是编译不通过的,违反了继承性。
但是我们放在集合中,去实例化,是无法通过的
List<Bird> birdList1 = new List<Bird>();
//List<Bird> birdList2 = new List<Sparrow>();//不是父子关系,没有继承关系
//一群麻雀一定是一群鸟
那么我们如何去实现在泛型中的继承性呢??这就引入了协变和逆变得概念,为了保证类型的安全,C#编译器对使用了 out 和 in 关键字的泛型参数添加了一些限制:
- 支持协变(
out)的类型参数只能用在输出位置:函数返回值、属性的get访问器以及委托参数的某些位置 - 支持逆变(
in)的类型参数只能用在输入位置:方法参数或委托参数的某些位置中出现。
引用文献:
https://www.cnblogs.com/wangbaicheng1477865665/p/OutIn.html 王柏成
https://www.cnblogs.com/kudsu/p/7727615.html 发配三千里

浙公网安备 33010602011771号