http://msdn.microsoft.com/zh-cn/library/zcx1eb1e(v=vs.100).aspx
通用类型系统定义了如何在公共语言运行时中声明、使用和管理类型,同时也是运行时跨语言集成支持的一个重要组成部分。常规类型系统执行以下功能:
建立一个支持跨语言集成、类型安全和高性能代码执行的框架。
提供一个支持完整实现多种编程语言的面向对象的模型。
定义各语言必须遵守的规则,有助于确保用不同语言编写的对象能够交互作用。
本主题包含以下各节:
.NET Framework 中的所有类型不是值类型就是引用类型。
值类型是使用对象实际值来表示对象的数据类型。如果向一个变量分配值类型的实例,则该变量将被赋以该值的全新副本。
引用类型是使用对对象实际值的引用(类似于指针)来表示对象的数据类型。如果为某个变量分配一个引用类型,则该变量将引用(或指向)原始值。不创建任何副本。
.NET Framework 中的通用类型系统支持以下五种类别的类型:
类
类是可以直接从另一个类派生以及从 System.Object 隐式派生的引用类型。类定义对象(它是该类的实例)可以执行的操作(方法、事件或属性)和该对象包含的数据(字段)。
下表介绍了类可以具有的一些特征。支持运行时的每种语言都提供了一种方法,来指示类或类成员具有其中的一种或多种特征。但是,针对 .NET Framework 的各个编程语言不能使所有这些特征都可用。
特征 | 说明 |
---|---|
sealed | 指定不能从此类型派生出另一个类。 |
implements | 指出该类通过提供接口成员的实现,使用一个或多个接口。 |
abstract | 指出无法实例化类。若要使用它,必须由其派生出另一个类。 |
inherits | 指出可以在指定了基类的任何地方使用类的实例。从基类继承的派生类可以使用基类提供的任何公共成员的实现,派生类也可以用自己的实现重写这些公共成员的实现。 |
exported 或 not exported | 指出某个类在定义它的程序集之外是否可见。此特征仅适用于顶级类,不适用于嵌套类。 |
![]() |
---|
类也可以嵌套在父类或结构中。嵌套类也有成员特征。有关更多信息,请参见嵌套类型。 |
没有实现的类成员是抽象成员。有一个或更多抽象成员的类其本身也是抽象的;不可以创建它的新实例。以运行时为目标的某些语言允许将类标记为抽象类,即使其成员都不是抽象成员也是如此。当要封装一组派生类可在适当时候继承或重写的基本功能时,可以使用抽象类。非抽象的类称为具体类。
类可以实现任意数目的接口,但是它除了 System.Object(所有类都可以隐式从它继承)之外,只能从一个基类继承。所有的类都必须至少有一个构造函数,该函数初始化此类的新实例。如果没有显式定义构造函数,大多数编译器将自动提供一个默认(无参数的)构造函数。
结构
结构是隐式从 System.ValueType 派生的值类型,后者则是从 System.Object 派生的。对于表示内存要求很小的值以及将值作为按值参数传递给具有强类型参数的方法,结构很有用。在 .NET Framework 类库中,所有基元数据类型(Boolean、Byte、Char、DateTime、Decimal、Double、Int16、Int32、Int64、SByte、Single、UInt16、UInt32 和 UInt64)都定义为结构。
像类一样,结构同时定义数据(结构的字段)和可以对该数据执行的操作(结构的方法)。这意味着可以对结构调用方法,包括在 System.Object 和 System.ValueType 类上定义的虚方法以及在值类型自身上定义的任意方法。换句话说,结构可以具有字段、属性和事件以及静态和非静态方法。您可以创建结构的实例,将它们作为参数传递,将它们存储为局部变量,或将它们存储在另一值类型或引用类型的字段中。结构也可以实现接口。
值类型与类在一些方面也是不同的。首先,尽管它们都从 System.ValueType 隐式继承,但是它们不能从任何类型直接继承。同样,所有值类型都是密封的,这意味着不能从它们派生出其他类型。它们也不需要构造函数。
对于每一种值类型,公共语言运行时都提供一种相应的已装箱类型,它是与值类型有着相同状态和行为的类。将值类型的实例传递到接受 System.Object 类型的参数的方法时,会将它装箱。当控件从接受值类型作为传引用参数的方法调用返回时,会将它拆箱(即它从类实例重新转换回值类型的实例)。当需要已装箱的类型时,某些语言要求使用特殊的语法;而另外一些语言会在需要时自动使用已装箱的类型。在定义值类型时,需要同时定义已装箱和未装箱的类型。
枚举
枚举 (enum) 是一种值类型,该值类型直接从 System.Enum 继承并为基础基元类型的值提供替代名称。枚举类型具有一个名称、一个必须为某个内置带符号或不带符号的整数类型的基础类型(如 Byte、Int32 或 UInt64)以及一组字段。字段是静态文本字段,其中的每一个字段都表示常数。同一个值可以分配给多个字段。出现这种情况时,必须将其中某个值标记为主要枚举值,以便进行反射和字符串转换。
可以将基础类型的值分配给枚举,反之亦然(运行时不要求强制转换)。可以创建枚举的实例,并调用 System.Enum 的方法以及在枚举的基础类型上定义的任何方法。但是,某些语言可能不允许在要求基础类型的实例时将枚举作为参数传递(反之亦然)。
对于枚举还有以下附加限制:
它们不能定义自己的方法。
它们不能实现接口。
它们不能定义属性或事件。
枚举不能是泛型,除非它嵌套在泛型类型中,才能是泛型。也就是说,枚举不能有自己的类型参数。
注意
用 Visual Basic、C# 和 C++ 创建的嵌套类型(包括枚举)包含所有封闭泛型类型的类型参数,因此即使这些嵌套类型没有自己的类型参数,它们也是泛型的。有关更多信息,请参见 Type.MakeGenericType 参考主题中的“嵌套类型”。
FlagsAttribute特性表示一种特殊的枚举,称为位域。运行时本身不区分传统枚举与位域,但您的语言可能会区分二者。当区分二者的时候,可以对位域(而不是枚举)使用位操作符以产生未命名的值。枚举一般用于列出唯一的元素,如一周的各天、国家/地区名称,等等。位域一般用于列出可能联合发生的质量或数量,比如 Red And Big And Fast。
下面的示例说明如何使用位域和传统枚举。
using System; using System.Collections.Generic; // A traditional enumeration of some root vegetables.publicenum SomeRootVegetables { HorseRadish, Radish, Turnip } // A bit field or flag enumeration of harvesting seasons. [Flags] publicenum Seasons { None = 0, Summer = 1, Autumn = 2, Winter = 4, Spring = 8, All = Summer | Autumn | Winter | Spring } publicclass Example { publicstaticvoid Main() { // Hash table of when vegetables are available. DictionaryAvailableIn = new Dictionary (); AvailableIn[SomeRootVegetables.HorseRadish] = Seasons.All; AvailableIn[SomeRootVegetables.Radish] = Seasons.Spring; AvailableIn[SomeRootVegetables.Turnip] = Seasons.Spring | Seasons.Autumn; // Array of the seasons, using the enumeration. Seasons[] theSeasons = new Seasons[] { Seasons.Summer, Seasons.Autumn, Seasons.Winter, Seasons.Spring }; // Print information of what vegetables are available each season.foreach (Seasons season in theSeasons) { Console.Write(String.Format( "The following root vegetables are harvested in {0}:\\n", season.ToString("G"))); foreach (KeyValuePair item in AvailableIn) { // A bitwise comparison.if (((Seasons)item.Value & season) > 0) Console.Write(String.Format(" {0:G}\\n", (SomeRootVegetables)item.Key)); } } }}// The example displays the following output:// The following root vegetables are harvested in Summer:// HorseRadish// The following root vegetables are harvested in Autumn:// Turnip// HorseRadish// The following root vegetables are harvested in Winter:// HorseRadish// The following root vegetables are harvested in Spring:// Turnip// Radish// HorseRadish
接口
接口定义用于指定“可以执行”关系或“具有”关系的协定。接口通常用于实现某种功能,如比较和排序(IComparable 和 IComparable
对接口有以下限制:
接口可以用任何可访问性来声明,但接口成员必须全都具有公共可访问性。
接口不能定义构造函数。
接口不能定义字段。
接口只能定义实例成员。它们不能定义静态成员。
每种语言都必须提供映射规则,将实现映射到需要该成员的接口,因为多个接口可以使用同一个签名声明成员,而这些成员可以分别具有单独的实现。
委托
委托是用途类似于 C++ 中的函数指针的引用类型。它们用于 .NET Framework 中的事件处理程序和回调函数。与函数指针不同,委托是安全、可验证和类型安全的。委托类型可以表示任何具有兼容签名的实例方法或静态方法。
如果委托参数的类型的限制性强于方法参数的类型,则该委托的参数与该方法的相应参数兼容,因为这可保证传递给委托的参数可以安全地传递给方法。
同样,如果方法的返回类型的限制性强于委托的返回类型,则该委托的返回类型与该方法的返回类型兼容,因为这可保证方法的返回值可以安全地强制转换为委托的返回类型。
例如,具有类型为 IEnumerable 的参数和 Object 返回类型的委托可以表示具有类型为 Object 的参数和类型为 IEnumerable 的返回值的方法。有关更多信息及代码示例,请参见Delegate.CreateDelegate(Type, Object, MethodInfo)。
委托一般绑定到它表示的方法。除了绑定到方法之外,委托还可以绑定到对象。该对象表示方法的第一个参数,每次调用委托时将该对象传递给方法。如果方法是实例方法,则将绑定的对象作为隐式 this 参数(在 Visual Basic 中为 Me)传递;如果方法为静态方法,则将该对象作为方法的第一个形参传递,并且委托签名必须匹配其余参数。有关更多信息及代码示例,请参见System.Delegate。
所有委托从 System.MulticastDelegate(继承自 System.Delegate)继承。C#、Visual Basic 和 C++ 语言不允许从这些类型继承,而是提供了用于声明委托的关键字。
由于委托从 MulticastDelegate 继承,因此委托具有一个调用列表,其中列出了委托表示的方法,在调用委托时将执行该列表中的方法。列表中的所有方法接收调用委托时提供的参数。
![]() |
---|
没有为在调用列表中包含多个方法的委托(即使委托具有返回类型)定义返回值。 |
在许多情况下(例如使用回调方法),一个委托只表示一个方法,而您需要做的就是创建委托并调用它。
对于表示多个方法的委托,.NET Framework 提供了 Delegate 和 MulticastDelegate 委托类的方法以支持如下操作:将方法添加到委托的调用列表(Delegate.Combine 方法)、移除方法(Delegate.Remove 方法)、获取调用列表(Delegate.GetInvocationList 方法)。
![]() |
---|
不需要将这些方法用于 C#、C++ 和 Visual Basic 中的事件处理程序委托,因为这些语言为添加和移除事件处理程序提供了语法。 |