拥有进入点(entry point)的程序集称应用程序(application)。当运行一应用程序时,将创建一新应用程序域(application domain)。同一个应用程序可在同一台机器(machine)上同时运行多个实例,并且每个实例都有自己的应用程序域。
应用程序域作为应用程序状态(application state)之容器(container),使应用程序相互隔离(isolation)。应用程序域是定义于应用及所用类库的类型之容器与边界。加载入不同的应用程序域的同一类型是相互泾渭分明的,而其实例化的对象也不会在应用程序域之间直接共享。比方说,对于这些类型的静态变量,每个应用程序域都自有一份其副本,同时这些类型的静态构造函数在应用程序域中至多运行一次。实现可以自由为应用程序域的创建和销毁提供指定实现(implementation-specific)策略或机制。
应用程序启动(Application startup)时,执行环境会调用一个特指的方法作为应用程序的进入点(entry point)。入口点方法一贯称为 Main,且可为下列签名中的一种:
static void Main() {...}
static void Main(string[] args) {...}
static int Main() {...}
static int Main(string[] args) {...}
如上所示,进入点可以选择 int 为其返回值。这个返回值通常被用在应用程序终止(application termination,第三章第二节)时。
进入点有个可选形参。这个参数可以用任何名称,但它的类型必须是 string[]
。如果出现形参,那么当应用程序启动时,执行环境将通过命令行参数(command-line arguments)的方式创建并传递指定的 string[]
实参。实参 string[]
永不为空(null),但它可能长度为零(如果命令行没有指定实参的话)。
由于 C# 支持方法重构(overload),类或结构可以包含一个方法的多个定义,前提是每个重载版本都有不同的方法签名。然而,在一个程序内,类或结构内不能同时存在多个叫做 Main 的方法,因为 Main 被限定为只能作为应用程序的入口点。如果提供超过一个参数或者唯一参数类型不是 string[]
,则允许重载多个 Main 版本。
应用程序可由多个类与结构组成,也许会有多个 Main 方法在这些类或结构内,由于 Main 的定义限定它只能是应用程序入口点,所以在这种情况下,外部机制(诸如命令行编译器选项)必须从中选择其一作为应用程序的进入点。
在 C# 中每一个方法都必须定义为类或结构的成员,一般情况下方法的声明可访问性(declared accessibility,第三章第 5.1 节)是由访问控制修饰符(access modifiers,第十章第 3.5 节)所决定的;同样,类型的声明声明可访问性也是由其访问控制修饰符所决定的。为了调用所给定类型的给定方法,类型与成员都必须可被访问(accessible),然应用程序进入点是特例。不管应用程序进入点的声明可访问性及其闭包类型的声明可访问性,执行环境总能访问到它们。
应用程序进入点方法(entry point method)不能放入泛型类声明内。
在其它各方面,进入点方法与非进入点方法的行为类似。
应用程序终止将返回控制权给执行环境。
应用程序进入点方法的返回类型是 int
,该值作为应用程序终止状态代码(application's termination status code)返回。该代码的目的是允许与执行环境通讯以告知成功或失败。
如果进入点方法的返回类型是 void
,遇到该方法的关门大括号 }
或执行到一个没有表达式的返回语句(return statement),则终止状态代码为 0。
先于应用程序的终止,未被垃圾回收的对象的析构函数将被调用,除非这类清理工作已被取消(supPRessed)(比方说通过调用库方法GC.SuppressFinalize
)。
C# 程序中的声明定义了程序的组成元素。C# 程序有系统地使用了命名空间(namespace,第九章),命名空间可以包含类型声明(type declarations)和嵌套命名空间声明(nested namespace declarations)。类型声明(type declarations,第九章第六节)用于定义类(class,第十章)、结构(struct,第十章第十四节)、接口(interface,第十三章)、枚举(enum,第十四章)以及委托(delegates,第十五章)。类型声明中允许的成员种类取决于类型声明的形式。比方说类声明可以包含声明常量(constants,第十章第四节)、字段(fields,第十章第五节)、方法(methods,第十章第六节)、属性(properties,第十章第七节)、事件(events,第十章第八节)、索引器(indexers,第十章第九节)、操作符(Operators,第十章第十节)、实例构造函数(instance constructors,第十章第十一节)、静态构造函数(static constructors,第十章第十二节)、析构函数(destructors,第十章第十三节)和嵌套类型(nested types,第十章第 3.8 节)。
声明在其所属声明空间(declaration space)内定义一个名称。除重载成员(第三章第六节)外,同一声明空间(declaration space)内出现两个以上同名成员会引发「编译时错误(compile-time error)」。同一声明空不可同时包含不同类型的同名方法。比方说,声明空间绝无包含同名字段与方法之可能。
有数种不同的声明空间,其具体描述如下:
namespace-declaration
内的 namespace-member-declarations
是属于一个叫做全局声明空间(global declaration space)的组合声明空间内的成员;namespace-declarations
内的 namespace-member-declarations
若拥有相同的完全限定命名空间名,则其属于同一个组合声明空间内;class-member-declarations
、struct-member-declarations
、interface-member-declarations
或 type-parameters
引入此声明空间。除了重载实例构造函数和静态构造函数,类或结构不允许声明包含一个与其类名或结构名同名的成员。类、结构或结构允许重载声明方法和索引器。此外类或结构允许重载声明实例构造函数与操作符。比方说类、结构或接口可以包含多个声明为同名的方法,这些所声明的方法拥有不同的签名(signature,第三章第六节)。注意,基类并不在该类的声明空间内,基接口也不在该接口的声明空间内,所以派生类或接口允许声明一个与继承成员同名的成员。这类成员隐藏(hide)了它们继续的那些成员;fixed-parameters
与 parameter-arrays
以及 type-parameters
引入这个声明空间;enum-member-declarations
引入这个声明空间;fixed-parameters
和 parameter-arrays
以及 type-parameters
引入这个声明空间。如果有函数成员或匿名函数成员的函数体,则将会被是为嵌套于局部变量声明空间内。局部变量声明控件和嵌套局部变量声明空间内包含同名元素将引发错误。因此在一个闭包声明空间内,如果包含嵌套声明空间的话,那么不能在其内部声明一个与闭包声明空间内变量或常量同名的局部变量或常量。只有一种可能使得两个声明空间内包含同名元素,那就是这两个声明空间彼此不相互包含。block
或 switch-block
,以及 for
、foreach
和 using
语句将为局部变量和局部常量创建局部变量声明空间。名称将通过 local-variable-declarations
和 local-constant-declarations
引入这个声明空间。注意,作为函数成员主体或匿名函数主体的块,或是位于函数成员主体或匿名函数主体内的块,将嵌套在本地变量声明空间内(这个本地变量声明空间是为其参数所声明的)。因此,如果某个方法的局部变量和参数同名,那么将引发一个错误。block
或 switch-block
为每个标签(label)创建相互隔离的声明空间。名称将通过 labeled-statements
引入该声明空间,并通过 goto-statements
来引用。块的标签声明空间(label declaration space)可包含任何嵌套块。因此在嵌套块内不能声明一个与其所在闭包块(enclosing block)标签同名的标签。声明名称的文本顺序一般而言是不重要的(no significance)。具体而言,声明并使用命名空间、常量、方法、属性、事件、索引器、操作符、实例构造函数、析构函数、静态构造函数以及类型,其文本顺序并不重要。在以下情况下声明顺序比较重要:
when constant-expression
被省略时,枚举成员声明(第十四章第三节)的顺序是重要的。命名空间的声明空间是开放的(open ended),两个相同完全限定名(fully qualified name)的命名空间拥有同一个声明空间。比方说:
namespace Megacorp.Data{ class Customer { ... }}namespace Megacorp.Data{ class Order { ... }}
这两个命名空间位于同一个声明空间内,在此例中两个类的完全限定名分别是 Megacorp.Data.Customer
和 Megacorp.Data.Order
。因为这两个声明位于同一个声明空间内,所以如果这两个类的完全限定名是一样的话,会引发一个「编译时错误」。
如上文所指定,块(block)的声明空间可以嵌套任意块。因此,在下例中,F 方法和 G 方法会引发「编译时错误」,因为外部块中声明了 i,那么内部块就不能重复声明(redeclared)。然而 H 方法和 I 方法是合法的,这是由于两个 i 的声明都位于相互独立的非嵌套块(separate non-nested blocks)内。
class A{ void F() { int i = 0; if (true) { int i = 1; } } void G() { if (true) { int i = 0; } int i = 1; } void H() { if (true) { int i = 0; } if (true) { int i = 1; } } void I() { for (int i = 0; i < 10; i++) H(); for (int i = 0; i < 10; i++) H(); }}
命名空间和类型都拥有成员。在实体开始被引用时,实体成员一般都可通过限定名称(qualified name)引入其中,通过标记(token).
引出成员的名字。
类型的成员既可在类型声明中声明,也可从其基类中继承。当一个类型继承自其基类时,所有基类成员(除了实例构造函数、析构函数以及静态构造函数)都将成为派生类型的成员。基类成员的声明可访问性并不控制成员是否可被继承——继承可拓展到除实例构造函数、析构函数和静态构造函数之外的任意成员。然而。也有可能派生类型无法访问到所继承的成员,比方说因为其声明可访问性(第三章第 5.1 节)或是因为其通过类型自身声明隐藏(第三章第 7.1.2 节)。
如果命名空间与类型没有闭包于一个命名空间,则它们将是全局命名空间(global namespace)的成员。这相当于名字直接在全局声明空间内声明了。
如果命名空间与类型在一个命名空间内,那么命名空间和类型将是这个外部命名空间的成员。这意味着名字直接在这个命名空间的声明空间内声明了。
命名空间没有访问限制(access restrictions)。不可以为命名空间声明为 private、 protected 或 internal,命名空间永远是可公开取得的(publicly accessible)。
结构成员是结构内声明的成员,以及直接继承自结构基类 System.ValueType
以及间接继承自基类 object
的成员。
简单类型的成员通过类型别名(alias)直接对应结构类型的成员:
sbyte
的成员是 System.SByte
结构的成员;byte
的成员是 System.Byte
结构的成员;short
的成员是 System.Int16
结构的成员;ushort
的成员是 System.UInt16
结构的成员;int
的成员是 System.Int32
结构的成员;uint
的成员是 System.UInt32
结构的成员;long
的成员是 System.Int64
结构的成员;ulong
的成员是 System.UInt64
结构的成员;char
的成员是 System.Char
结构的成员;float
的成员是 System.Single
结构的成员;double
的成员是 System.Double
结构的成员;decimal
的成员是 System.Deci
新闻热点
疑难解答