今天我们来学习两个概念 CTS(Common Type System,通用类型系统)和 CLS(Common Language Specification,通用语言规范),以及了解一下它们的作用。
01 CTS 通用类型系统
一个程序集可能包含任意数量的不同类型。在 .NET 的世界里,类型只是一个一般的术语,用来指代类、接口、结构、枚举或委托(共五种)。当你使用 .NET 平台的语言进行编程时,你很可能会与许多类型进行交互。例如,你的程序集可能定义了某个类,实现了一些接口,可能其中一个方法需要一个枚举类型作为参数,并返回一个结构类型给调用者。
CTS(Common Type System,通用类型系统)是一个正式的规范,它记录了必须如何定义类型才能被 .NET 公共语言运行时(CLR)托管。
通常情况下,只有那些针对 .NET 平台开发工具或编译器的人,才会对 CTS 的内部工作原理感兴趣。对于几乎所有的 .NET 程序员来说,不需要对它进行深入研究。但对于 CTS 定义的五种类型,我们必须知道怎么使用你喜欢的语言(C#、VB 或 F#) 来定义它们。
以下基于 C# 简单的概述一下 CTS 的五种类型如何使用。
CTS 类类型
每一种 .NET 语言都至少支持类类型的概念,这是面向对象编程(OOP)的基石。一个类可以由任何数量的成员(如构造函数、属性、方法和事件)和数据字段组成。在 C# 中,类是用 class
关键字来声明的,像这样:
class Calc
{
public int Add(int num1, int num2)
{
return num1 + num2;
}
}
下面是 CTS 类类型的主要特征:
- 是否密封(Sealed):密封类不能被其它类型继承,即它不能作为其它类的基类。
- 是否实现接口:接口是一个抽象成员的集合,它在对象和对象的调用者之间提供了一种契约。CTS 允许一个类实现任何数量的接口。
- 是抽象(Abstract)还是具体(Concrete):抽象类不能被直接实例化,其目的是为派生类型定义通用行为。具体类可以直接被实例化。
- 可见性(Visibility):每个类都必须有一个可见性关键字,如
public
或internal
等。它控制着该类的可访问性,例如internal
表示不可以被外部程序集使用。
CTS 接口类型
接口是抽象成员的定义的集合(在 C# 8 中还新增了默认实现)。在 C# 中,接口类型是用 interface
关键字定义的。按照约定,所有的 .NET 接口都以大写字母 I
开头,如下面的例子:
public interface IDraw
{
void Draw();
}
就接口本身而言,接口没有什么用处。但,当多个类或结构以特定的方式实现了一个指定的接口时,你就能够以多态的方式使用接口提供的功能。
CTS 结构类型
简单来说,结构可以认为是一种具有基于值的轻量级类类型。通常情况下,结构最适合用于几何和数学数据的建模。在 C# 中使用 struct
关键字创建一个结构类型,如下所示:
struct Point
{
// 结构类型的字段
public int X, Y;
// 结构类型的带参构造函数
public Point(int x, int y)
{ X = x; Y = y;}
// 结构类型的方法
public void PrintPosition()
{
Console.WriteLine("({0}, {1})", X, Y);
}
}
CTS 枚举类型
枚举是一种很方便的结构,它允许你将键-值
对分组。例如,假设你正在开发一款游戏,则可以用枚举定义玩家的角色(如魔法师、战士和盗贼等)。C# 中使用 enum
关键字创建一个强类型的枚举,而不是用简单的数值来表示每个项,例如:
enum CharacterType
{
Wizard = 100,
Fighter = 200,
Thief = 300
}
默认情况下,枚举项的存储单位是一个 32 位的整型(System.Int32
),也可以改为其它的(如Sytem.Byte
)。另外,所有 CTS 枚举类型都派生自一个共同的基类–System.Enum
。
CTS 委托类型
.NET 的委托类型类似于 C 语言的函数指针。关键的区别在于,.NET 的委托是一个继承自 System.MulticastDelegate
的类,而不是一个指向原始内存地址的简单指针。在 C# 中,委托是用 delegate
关键字声明的:
delegate int Calc(int x, int y);
委托的作用在于:为一个对象提供一种方法来转发对另一个对象的调用,即它可以把对象的函数当作参数传递。它是 .NET 事件、多播(即把一个请求转发给多个接收者)和异步方法调用(即在一个线程上调用另一个线程的方法)的基础。
CTS 类型的成员
CTS 的大多类型都可以有任何数量的成员。从形式上讲,类型的成员的各类有:构造函数、终结器、静态构造函数、嵌套类型、运算符、方法、属性、索引器、字段、只读字段、常量和事件。
CTS 定义了可能与给定成员相关联的各种修饰符。例如,每个成员都有一个给定的可见性特征(例如,public
、private
、protected
等)。一些成员可以被声明为抽象的(在派生类型上强制执行多态行为)以及虚的(定义一个封装好的、可被重写的实现)。另外,大多数成员可以被配置为静态的(绑定在类的层面)或实例的(绑定在对象的层面)。
CTS 基本数据类型
CTS 建立了一套定义明确的基本数据类型。尽管不同的语言通常用不同的关键字声明基本数据类型,但所有的 .NET 平台的语言关键字最终都会被解析为相同的 CTS 类型,并定义在一个名为 mscorlib.dll
的程序集中。下表是 CTS 基本数据类型分别在 VB.NET 和 C# 中的表达:
语言特有的关键字只是 System 命名空间中真实类型的速记符号,它们本质上没有区别。考虑以下代码片段,它们在 C# 和 VB 中定义了 32 位整型变量,使用了语言关键字以及 CTS 数据类型:
// 在 C# 中定义 32 位整型
int i = 0;
System.Int32 j = 0;
// 在 VB 中定义 32 位整型
Dim i As Integer = 0
Dim j As System.Int32 = 0
02 CLS 通用语言规范
正如上面所演示的,不同的语言用自己独特的语法来表达相同的意思。例如,在 C# 中,用运算符 +
表示字符串连接,而在 VB 中通常用 &
符号表示。即使两种不同的语言表达了相同的意思(例如,一个没有返回值的函数),它们的语法可能是完全不同的,例如:
// 没有返回值的 C# 方法
public void MyMethod()
{
// Do something...
}
' 没有返回值的 VB 方法
Public Sub MyMethod()
' Do something...
End Sub
在 .NET 运行时眼中,这些语法的不同是不被感知的,因为各自的编译器(C# 是 csc.exe
,VB 是 vbc.exe
)会生成一组类似的 IL 代码。然而,语言也可以在其整体功能水平方面有所不同。例如,.NET 的语言中,有的可能支持无符号数值的关键字,有的可能不支持;有的可能支持支持指针类型,有的可能不支持。鉴于这些语言功能水平的不同,但又为了能够实现各个语言这间的互操作,所以需要划定一个基准来规定所有的 .NET 语言都要符合一定的规范。在这个规范内,各种 .NET 语言都可以互操作。
这样的一个规范就是 CLS(Common Language Specification,通用语言规范)。
CLS 是一套详细地描述了 .NET 编译器必须支持的最小功能集,以生成可由 .NET 运行时托管的代码,同时可由所有 .NET 平台的语言以统一方式访问。CLS 可以被视为 CTS 所定义的全部功能的一个子集。
CLS 规则示例
CLS 最终是一套规则,如果编译器开发者想让他们的产品在 .NET 平台无缝运行,就必须遵守这套规则。每条规则都有一个简单的名称(例如,CLS Rule 6),并描述了如何规范编译器的行为。CLS 的精华部分是 Rule 1:
Rule 1:CLS 规则只适用于一个类型中暴露在定义程序集之外的部分。
鉴于这条规则,你可以推断出 CLS 的其余规则并不适用于 .NET 类型的内部工作逻辑。类型中唯一必须符合 CLS 的是成员定义本身(即,命名约定、参数和返回类型)。成员的实现逻辑可以使用任意的非 CLS 技术,因为外部世界不会知道成员内部的细节。
这样说可能有点抽象,还是来举个例子吧。
下面这个 C# 的 Add()
方法不符合 CLS,因为参数和返回值使用了无符号数值(这不是 CLS 的要求):
class Calc
{
// 向外暴露无符号数值不遵从 CLS 规则
public ulong Add(ulong num1, ulong num2)
{
return num1 + num2;
}
}
然而,请考虑以下代码,它在一个方法内部使用了无符号数值:
class Calc
{
public int Add(int num1, int num2)
{
// 由于 ulong 类型的变量只在方法内部使用,
// 它仍然遵从 CLS 规则
ulong temp = 0;
...
return num1 + num2;
}
}
该方法仍然符合 CLS 的规则,可以放心地让所有 .NET 语言都能调用 Add()
方法。
当然,除了 Rule 1 之外,CLS 还定义了许多其他规则。例如,CLS 描述了特定语言必须如何表示文本字符串,枚举应该如何在内部表示(用于存储的基本类型),如何定义静态成员,等等。不过,你不必了解这些规则细则,实际开发中不会用到这些知识。
确保符合 CLS 规范
正如上面演示的,C# 确实定义了一些不符合 CLS 的编程结构。那怎么来保证你的 C# 代码符合 CLS 规范呢?你可以使用一个 .NET 特性来告诉 C# 编译器来检查你的代码是否符合 CLS 规范:
// 告诉 C# 编译器检查是否符合 CLS 规范
[assembly: CLSCompliant(true)]
这里的 [CLSCompliant]
属性将告诉 C# 编译器根据 CLS 的规则检查代码。如果发现违反 CLS 规则的行为,会收到一个编译器的警告和对违规代码的描述。
03 小结
本文探讨了 .NET 中的 CTS(通用类型系统)和 CLS(通用语言规范)。我们需要知道什么是 CTS 和 CLS,以及它们的作用,其它的细节不重要,了解一下即可。
本文来自http://cnblogs.com/willick,经授权后发布,本文观点不代表个人技术分享立场,转载请联系原作者。