C# 基础知识系列 专题七: 泛型深入理解(一)
最后更新于:2022-04-02 00:09:34
# [C# 基础知识系列]专题七: 泛型深入理解(一)
**引言:**
在上一个专题中介绍了C#2.0 中引入泛型的原因以及有了泛型后所带来的好处,然而上一专题相当于是介绍了泛型的一些基本知识的,对于泛型的性能为什么会比非泛型的性能高却没有给出理由,所以在这个专题就中将会介绍原因和一些关于泛型的其他知识。
**一、泛型类型和类型参数**
泛型类型和其他int,string一样都是一种类型,泛型类型有两种表现形式的:泛型**类型(包括类、接口、委托和结构,但是没有泛型枚举的)**和泛型方法。那什么样的类、接口、委托和方法才称作泛型类型的呢 ?我的理解是类、接口、委托、结构或方法中有**类型参数**就是泛型类型,这样就有类型参数的概念的。 **类型参数** ——是一个真实类型的一个占位符(**我想到一个很形象的比喻的,比如大家在学校的时候,一到中午下课的时候食堂人特别多的,所以很多应该都有用书本占位置的习惯的, 书本就相当于一个占位符,真真坐在位置上的当然是自己的,讲到占位置,以前听过我同学说,他们班有个很牛逼的MM,中午下完课的时候用手机占位子的,等它打完饭回来的时候手机已经不见, 当时听完我就和我同学说,你们班这位女生真牛逼的,后面我们就**![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-18_569ca4488de4a.gif)),泛型声明中,类型参数必须放在一对尖括号里面(即<>这个符号),并且用逗号分隔多个类型参数,如List<T>类中T就是**类型参数,**在**使用泛型类型或方法的时候,我们要用真实类型来代替**,就像用书本占位子一个,书本只是暂时的在那个位置上,等打好饭了就要换成你坐在位置上了,同样在C#中泛型也是同样道理,类型参数只是暂时的在那个位置,真真使用中要用真实的类型去代替它的位置,此时我们把真实类型又取名为**类型实参,**如上一专题的代码中List<int>,类型实参就是int(代替T的位置)。
如果没有为类型参数提供类型实参,此时我们就声明了一个未绑定的泛型类型,如果指定了类型实参,此时的类型就叫做已构造类型(这里同样可以以书占位置去理解),然而已构造类型又可以是**开放类型或封闭类型**的,这里先给出这个两个概念的定义的:**开放类型**——具有类型参数的类型就是开放类型(所有的未绑定的泛型类型都属于开放类型的),**封闭类型**——为每个类型参数都传递了实际的数据类型。对于开放类型,我们创建**开放类型的实例**。
_注意:在C#代码中,我们唯一可以看到未绑定泛型类型的地方(除了作为声明之外)就是在typeof操作符里。_
下面通过以下代码来更好的说明这点:
```
using System;
using System.Collections.Generic;
namespace CloseTypeAndOpenType
{
// 声明开放泛型类型
public sealed class DictionaryStringKey : Dictionary
{
}
public class Program
{
static void Main(string[] args)
{
object o = null;
// Dictionary<,>是一个开放类型,它有2个类型参数
Type t = typeof(Dictionary<,>);
// 创建开放类型的实例(创建失败,出现异常)
o = CreateInstance(t);
Console.WriteLine();
// DictionaryStringKey<>也是一个开放类型,但它有1个类型参数
t = typeof(DictionaryStringKey<>);
// 创建该类型的实例(同样会失败,出现异常)
o = CreateInstance(t);
Console.WriteLine();
// DictionaryStringKey是一个封闭类型
t = typeof(DictionaryStringKey);
// 创建封闭类型的一个实例(成功)
o = CreateInstance(t);
Console.WriteLine("对象类型 = " + o.GetType());
Console.Read();
}
// 创建类型
private static object CreateInstance(Type t)
{
object o = null;
try
{
// 使用指定类型t的默认构造函数来创建该类型的实例
o = Activator.CreateInstance(t);
Console.WriteLine("已创建{0}的实例", t.ToString());
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
return o;
}
}
}
```
运行结果为(从结果中也可以看出开放类型不能创建该类型的一个实例,异常信息中指出类型中包含泛型参数):
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-23_56a2eb2a0ae52.png)
**二、泛型类型中的静态字段和静态构造函数**
首先实例字段是属于一个实例的,静态字段是从属于它们声明的类型,即如果在某个Myclass类中声明了一个静态字段field,则不管创建Myclass的多少个实例,也不管从Myclass中派生出多少个实例,都只有一个Myclass.x字段。然而**每个封闭类型都有它自己的静态字段(使用类型实参时,实际上CLR会定义一个新的类型对象, 所以每个静态字段都是不一样对象里面的静态字段,所以才会每个都有各自的值)** 通过以下代码来更好说明下——每个封闭类型都有它自己的静态字段:
View Code
```
namespace GenericStaticFieldAndStaticFunction
{
// 泛型类,具有一个类型参数
public static class TypeWithStaticField
{
public static string field;
public static void OutField()
{
Console.WriteLine(field+":"+typeof(T).Name);
}
}
// 非泛型类
public static class NoGenericTypeWithStaticField
{
public static string field;
public static void OutField()
{
Console.WriteLine(field);
}
}
class Program
{
static void Main(string[] args)
{
// 使用类型实参时,实际上CLR会定义一个新的类型对象
// 所以每个静态字段都是不一样对象里面的静态字段,所以才会每个都有各自的值
// 对泛型类型类的静态字段赋值
TypeWithStaticField.field = "一";
TypeWithStaticField.field = "二";
TypeWithStaticField.field = "三";
// 此时filed 值只会有一个值,每个赋值都是改变了原来的值
NoGenericTypeWithStaticField.field = "非泛型类静态字段一";
NoGenericTypeWithStaticField.field = "非泛型类静态字段二";
NoGenericTypeWithStaticField.field = "非泛型类静态字段三";
NoGenericTypeWithStaticField.OutField();
// 证明每个封闭类型都有一个静态字段
TypeWithStaticField.OutField();
TypeWithStaticField.OutField();
TypeWithStaticField.OutField();
Console.Read();
}
}
}
```
运行结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-23_56a2eb2a1e2e0.png)
同样**每个封闭类型都有一个静态构造函数的**,通过下面的代码可以让大家更加明白这点:
```
// 静态构造函数的例子
public static class Outer
{
// 嵌套类
public class Inner
{
// 静态构造函数
static Inner()
{
Console.WriteLine("Outer<{0}>.Inner<{1}>", typeof(Tx), typeof(Ty));
}
public static void Print()
{
}
}
}
class Program
{
static void Main(string[] args)
{
#region 静态函数的演示
// 静态构造函数会运行多次
// 因为每个封闭类型都有单独的一个静态构造函数
Outer.Inner.Print();
Outer.Inner.Print();
Outer.Inner.Print();
Outer.Inner.Print();
Outer
';