类型定义包括以下内容:

  • 对该类型定义的任何特性。

  • 类型的可访问性(可见性)。

  • 类型的名称。

  • 类型的基本类型。

  • 该类型实现的任何接口。

  • 每个类型的成员的定义。

特性

特性提供附加的用户定义元数据。它们通常用于在程序集中存储有关类型的附加信息,或在设计时或运行时环境中用于修改类型成员的行为。

特性本身是从 System.Attribute 继承的类。每种支持使用特性的语言都有自己的语法,用于将特性应用到某个语言元素。特性可应用到几乎任意语言元素;特性可以应用到的特定元素由应用到该特性类的 AttributeUsageAttribute 定义。

类型可访问性

所有类型都有一个修饰符,控制从其他类型对它们的可访问性。下表说明了运行时所支持的类型可访问性。

辅助功能

说明

public

所有程序集都可以访问此类型。

程序集

只能在其程序集内访问此类型。

嵌套类型的可访问性依赖于它的可访问域,该域是由已声明的成员可访问性和直接包含类型的可访问域这二者共同确定的。但是,嵌套类型的可访问域不能超出包含类型的可访问域。

在程序 P 的类型 T 中,声明的嵌套成员 M 的可访问域是按以下方法定义的(注意,M 本身也可能是一个类型):

  • 如果 M 的已声明可访问性为 public,则 M 的可访问域就是 T 的可访问域。

  • 如果 M 的已声明可访问性为 protected internal,则 M 的可访问域就是 T 的可访问域与 P 的程序文本和从 T 派生的(在 P 之外声明的)任何类型的程序文本之间的交集。

  • 如果 M 的已声明可访问性为 protected,则 M 的可访问域就是 T 的可访问域与 T 的程序文本和从 T 派生的任何类型的程序文本之间的交集。

  • 如果 M 的已声明可访问性为 internal,则 M 的可访问域就是 T 的可访问域与 P 的程序文本之间的交集。

  • 如果 M 的已声明可访问性为 private,则 M 的可访问域就是 T 的程序文本。

类型名称

常规类型系统对名称只有两种限制:

  • 所有的名称都按 Unicode(16 位)字符串进行编码。

  • 名称不允许有嵌入的(16 位)0x0000 值。

但是,大多数语言对类型名称都有附加限制。所有比较都是逐字节进行的,因此会区分大小写并与区域设置无关。

尽管类型可能引用来自其他模块和程序集的类型,但类型在一个 .NET Framework 模块内必须是完全定义的。(但是,根据编译器支持的情况,它可以分成多个源代码文件。)类型名称只需要在命名空间内唯一。要完全标识一个类型,其类型名称必须由包含此类型实现的命名空间加以限定。

基本类型和接口

一个类型可以从另一个类型继承值和行为。常规类型系统不允许类型从多个基本类型进行继承。

一个类型可以实现任何数量的接口。要实现接口,类型必须实现该接口的所有虚拟成员。虚方法可以由派生的类型来实现,既可静态调用,也可动态调用。

返回页首

运行时允许您定义指定类型行为和状态的类型成员。类型成员包括以下内容:

字段

字段描述并包含类型状态的一部分。字段可以是运行时支持的任何类型。字段通常是 privateprotected,因此只能在类内部或从派生类访问。如果可以从类型外部修改字段值,通常使用属性集访问器。公开的字段通常是只读的,可以为以下两种类型:

  • 常量,在设计时赋值。尽管没有使用 static(在 Visual Basic 中为 Shared)关键字定义,但它们都是类的静态成员。

  • 只读变量,可以在类构造函数中赋值。

下面的示例演示只读字段的这两种用法。

C#
VB
using System;

publicclass Constants
{
   publicconstdouble Pi = 3.1416;
   publicreadonly DateTime BirthDate;

   public Constants(DateTime birthDate)
   {
      this.BirthDate = birthDate;
   }
}

publicclass Example
{
   publicstaticvoid Main()
   {
      Constants con = new Constants(new DateTime(1974, 8, 18));
      Console.Write(Constants.Pi + "\\n");
      Console.Write(con.BirthDate.ToString("d") + "\\n");
   }
}
// The example displays the following output if run on a system whose current// culture is en-US://    3.1417//    8/18/1974

属性

属性命名类型的值或状态,并定义获得或设置属性值的方法。属性可以是基元类型、基元类型的集合、用户定义的类型或用户定义类型的集合。属性通常用于使类型的公共接口独立于类型的实际表示形式。这使属性能够反映不直接在类中存储的值(例如,当属性返回计算值时)或能够在将值赋给私有字段前进行验证。下面的示例演示后一种模式。

C#
VB
using System;

publicclass Person
{
   privateint m_Age;

   publicint Age
   { 
      get { return m_Age; }
      set {
         if (value < 0 || value > 125)
         {thrownew ArgumentOutOfRangeException("The value of the Age property must be between 0 and 125.");
         }
         else {
            m_Age = value;
         }         
      }
   }
}


除了包含属性本身之外,包含可读属性的类型的 Microsoft 中间语言 (MSIL) 还包含一个 get_属性名 方法,包含可写属性的类型的 MSIL 还包含一个 set_属性名 方法。

方法

方法描述可用于类型的操作。方法的签名指定其所有参数和返回值可使用的类型。

尽管大多数方法都定义方法调用所需的参数的确切数目,但某些方法支持可变数目的参数。这些方法的最后声明的参数用 ParamArrayAttribute 特性标记。语言编译器通常会提供一个关键字(例如,在 C# 中为 params,在 Visual Basic 中为 ParamArray),使得不必显式使用 ParamArrayAttribute

构造函数

构造函数是一种特殊类型的方法,可创建类或结构的新实例。像任何其他方法一样,构造函数可以包含参数,但是它不返回值(即它返回 void)。

如果类的源代码没有显式定义构造函数,编译器将包含一个默认(无参数的)构造函数。但是,如果某个类的源代码只定义参数化的构造函数,则 Visual Basic 和 C# 编译器将不会生成无参数构造函数。

如果某个结构的源代码定义多个构造函数,则这些构造函数必须是参数化的;结构不能定义默认(无参数)构造函数,并且编译器不会为结构或其他值类型生成无参数构造函数。所有值类型都具有隐式的默认构造函数。此构造函数由公共语言运行时实现,并且将该结构的所有字段都初始化为其默认值。

事件

事件定义可以响应的事情,并定义订阅、取消订阅及引发事件的方法。事件通常用于通知其他类型的状态改变。有关更多信息,请参见处理和引发事件

嵌套类型

嵌套类型是作为某其他类型的成员的类型。嵌套类型应与其包含类型紧密关联,并且不得用作通用类型。在声明类型使用和创建嵌套类型实例时,嵌套类型很有用,但不在公共成员中公开嵌套类型的使用。

嵌套类型可能会使有些开发人员感到困惑,因此除非有必要的理由,否则嵌套类型不应是公开可见的。在设计完善的库中,开发人员几乎不需要使用嵌套类型实例化对象或声明变量。

返回页首

通用类型系统允许类型成员具有多种特征,但并不要求语言能支持所有这些特征。下表介绍了这些成员特征。

特征

可应用到

说明

abstract

方法、属性和事件

类型不提供方法的实现。继承或实现抽象方法的类型必须提供方法的实现。只有当派生的类型本身是抽象类型的时候,情况例外。所有的抽象方法都是虚的。

private、family、assembly、family 和 assembly、family 或 assembly,或者 public

全部

定义成员的可访问性:

private

只能在与成员相同的类型或在嵌套类型中访问。

family

在与成员相同的类型中和从它继承的派生类型访问。

assembly

只能在定义该类型的程序集中访问。

family 和 assembly

只能从同时具备族和程序集访问权的类型进行访问。

family 或 assembly

只能从具备族和程序集访问权的类型进行访问。

public

可从任何类型访问。

final

方法、属性和事件

虚方法不能在派生类型中被重写。

initialize-only

字段

该值只能被初始化,不能在初始化之后写入。

instance

字段、方法、属性和事件

如果成员未标记为 static(C# 和 C++)、Shared (Visual Basic)、virtual(C# 和 C++)或 Overridable (Visual Basic),则它是一个实例成员(没有实例关键字)。内存中这些成员的副本数将会像使用它们的对象数一样多。

literal

字段

分配给该字段的值是一个内置值类型的固定值(在编译时已知)。文本字段有时指的是常数。

newslot 或 override

全部

定义成员如何与具有相同签名的继承成员进行交互:

newslot

隐藏具有相同签名的继承成员。

override

替换继承的虚方法的定义。

默认为 newslot。

static

字段、方法、属性和事件

成员属于定义它的类型,而不属于该类型的特定实例;即使不创建类型的实例,成员也会存在,并且它由该类型的所有实例共享。

virtual

方法、属性和事件

此方法可以由派生类型实现,并且既可静态调用,也可动态调用。如果使用动态调用,在运行时执行调用的实例类型(而不是编译时已知的类型)将确定调用方法的哪一种实现。若要静态调用虚方法,可能必须将变量强制转换为使用所需方法版本的类型。

重载

每个类型成员都有一个唯一的签名。方法签名由方法名称和一个参数列表(方法的参数的顺序和类型)组成。可以在一种类型内定义具有相同名称的多种方法,只要这些方法的签名不同。当定义两种或多种具有相同名称的方法时,就称作重载。例如,在 System.Char 中,重载了 IsDigit 方法。一个方法采用 Char另一个方法采用 StringInt32

\"注意\"注意

返回类型不被视为方法签名的一部分。这意味着如果方法只是返回类型不同,就不能重载。

继承,重写和隐藏成员

派生类型继承其基类型的所有成员;也就是说,会在派生类型上定义这些成员,并供派生类型使用。继承成员的行为和质量可以通过以下两种方式来修改:

  • 派生类型可通过使用相同的签名定义一个新成员,从而隐藏继承的成员。将先前的公共成员变成私有成员,或者为标记为 final 的继承方法定义新行为时,可以采取这种方法。

  • 派生类型可以重写继承的虚方法。重写方法提供了对方法的一种新定义,将根据运行时的值的类型,而不是编译时已知的变量类型来调用方法。只有在虚拟方法没有标记为 final 且新方法至少可以像虚拟方法一样进行访问的情况下,方法才能重写虚拟方法。