MSDN:基本概念
一、启动应用
1.具有入口点(entry point)的程序(assembly)称为应用(application) ,当一个应用运行时,便创建新的应用域(domain)。域作为应用状态(application state)的容器起到隔离应用(application isolation)的作用。应用域作为应用和它使用的类库(class libraries)中所定义的类型的容器和边界而存在,相同的类型加载到不同的应用域则各自独立。
2.执行环境(executive environment)调用指定的方法时(designated method)--即程序的入口点,应用程序开始启动。
3.入口点方法总是被命名为Main,可以具有以下签名:
static void Main() {...} static void Main(string[] args) {...} static int Main() {...} static int Main(string[] args) {...}
4.入口点允许一个可选的形参(formal parameter),但它的类型必须是string[],如果存在形参,则永不为null;
5.虽然c#中允许方法重载(overloading),通过不同的签名,使类或结构的方法具有同名的多个定义。但在单一的程序(program)中,设计来作为应用入口点的Main方法最多只能有一个;允许Main的其它重载版本,前提是它们有一个以上的参数,或参数类型不是字符串型;
6.应用程序可由多个类或结构组成,当其中含有多个作为程序入口点的Main方法时,必须通过外部方式,如命令行编译器选项,来从这些Main方法中选择一个作为程序进入点。
7.执行环境可以访问应用程序的入口点,无论其自身的可访问性和封闭它的类型声明的可访问性如何。即,无论方法自身和方法所在的类型声明的访问性如何,Main都能够被访问。
8.在所有其它方面,入口点方法的行为与其它非程序入口点方法类似。
二、终止应用
1.'应用终止'把控制权交还给执行环境;
2.如果程序入口点方法的返回类型为整数,则返回值作为应用终止的状态码。状态码,用于与程序执行环境就程序运行状态(成功,还是失败)进行通信;
3.如果程序入口点方法的返回类型为void时,在到达右括号"}"或执行无表达式的return后,则终止程序。导致终止状态码为0;
4.在程序终止前,没有进行垃圾回收的所有对象,会自动调用析构方法;除非这种清理机制被(使用)Gc.SuppressFinalize这样的类库方法所禁止。
三、声明(declarations)
C#程序的声明定义了程序的组成成员。c#程序使用名字空间组织程序,包括类型声明和嵌套类型声明。
1.类型声明用于定义类、结构、接口、枚举、委托;类型声明中允许哪种类型作为其成员(kinds of members permitted )是由类型声明的形式决定。如,类(class)声明中可以包括常量、字段、方法、属性、事件、索引器、运算符、实例构建器(instance constructor)、静态构建器、析构器,和嵌套类型的声明;
2.声明在其所属的空间中定义名字,在声明空间内有两个以上(包括两个)的声明引入同名的成员时,会导致编译时错误;声明空间中不允许不同种类的成员拥有相同的名称,如同名的字段与方法;
3.名称定义空间分为若干种:
(1)程序的所有源文件中,“名字空间成员声明”(namespace-member-declaration)没有被置于(enclose)命名空间内的,则它是“单组合(single combined)声明空间”(又称作“全局命名空间”)的成员;
(2)程序的所有源文件中,“名字空间成员声明”在“名字空间声明”内具有相同的完全限定(same fully qualified )的名字空间名称,是“单组合声明空间”;
(3)每个类、结构或接口定义创建一个新声明空间(declaration space),名称(names)是由“类成员声明”、“结构成员声明”、或“接口成员声明”引入到声明空间的;
a.除重载的实例构建器和静态构建器声明以外,类或结构的成员声明不允许引入同名的类成员或结构成员;
b.类、结构或接口,允许重载的方法和索引器声明,进一步(furthermore),允许重载的实例构建器和运算符的声明。如,只要类或结构中方法的签名不同,便允许同名的多个方法存在。
c.基类并不参与(contribute to)类的声明空间,基接口也不参与接口的声明空间;故,派生的类或接口允许声明与继承的成员同名的(一个)新成员,该成员被称为隐藏继承成员(hide inherited memeber)的成员;
(4)每个枚举定义创建一个新声明空间,通过枚举成员声明将名字引入声明空间;
(5)每个块(block)或者switch块为本地变量和常量创建一个不同的声明空间,通过本地变量声明或本地常量声明将名字引入声明空间。
a.如果块是实例构建器、方法、运算符声明、索引器声明的get及set访问器的体(body),那么在这样的声明中所声明的参数就是块的本地变量声明空间(block's local variable declaration space)的成员;
b.块的本地变量声明空间允许嵌套块;
c.被嵌套的块中的本地变量,不可能与上层块中的本地变量同名;
d.每个块或者switch块给标签(labels)声明不同的(separate)声明空间,名字通过标签语句(labeled-statement)引入声明空间,通过goto语句来使用;块的标签声明空间包括任何嵌套块;所以,在被嵌套的块和封闭它的块中不能使用同名的标签;
4.名字被声明的顺序并不重要,如声明和使用命名空间、常数、方法、属性、事件、索引器、运算符、实例构造函数、析构函数、静态构造函数和类型时,与文本顺序无关。
但在如下情况下,文本顺序(textual order)有意义:
(1)对于字段声明和本地变量声明,它们的声明顺序决定了初始化项(initializer)的顺序;
(2)本地变量在使用前必须被定义;
(3)当常量表达式值被忽略的时候(constant-expression values are omitted),“枚举成员声明”的声明顺序有意义。
在下面的示例中,F 和 G 方法导致编译时错误,因为名称 i 是在外部块中声明的,不能在内部块中重新声明。但方法 H 和 I 都是有效的,因为这两个 i 是在单独的非嵌套块中声明的。 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(); } }
四、成员的访问性
1.访问修饰符
(1)public,访问不受限制(not limited);
(2)protected,访问受限于它所属的类(containing)或从它所属的类派生的类型,而不能跨'类'访问;
(3)internal,访问受限在本程序(program)范围内;
(4)protected internal,访问限定于本程序或它所属的类派生的类型;
(5)private,访问受限于所属类型(containing type);
2.根据成员定义时的上下文(context)环境,确定访问性的使用。另外,在没有使用修饰符时,上下文环境会根据情况自动为其分配缺省的访问符。
(1)名字空间是隐式地public访问性(declared accessibility),不允许显式修饰符;
(2)编译单元或名字空间中声明的类型,可以使用public或internal访问,缺省为internal访问;
(3)类成员可以有五种访问性之一,缺省是private;(声明为类成员的类型,可以使用五种访问性。但作为名字空间成员的类型,就只能使用public或internal)
(4)结构成员可以使用三种访问性,分别是public、private、internal;缺省使用private,结构是隐式密封的(sealed);
a.结构的成员若是在此结构中声明的(也就是说,不是由该结构从它的基类中继承的)不能具有 protected
或 protected
internal
访问性。
b.声明为结构成员的类型可具有 public
、internal
或 private
访问性,而声明为名字空间成员的类型只能具有 public
或 internal
访问性。
(5)接口成员是隐式地public访问性,不允许使用显式访问符;
(6)枚举成员是隐式地public访问性,不允许使用显式访问符。
3.(可)访问域(accessibility domain),简言之,能够访问到目标的范围。
(1) 成员的访问域是由(可能不连续的)程序文本节组成的,从文本节可以访问该成员。出于定义成员访问域的目的,不在类型中声明的成员是'顶级的'(top-level),在其它类型中声明的成员是'嵌套的'。
(2)程序的(program)程序文本被定义为,程序的所有源文件包含的所有程序文本。类型的(type)程序文本被定义为,在类型(包括允许的类型的嵌套类型)的类体、结构体、接口体、枚举体中,包含在标记'{'和'}'之间的所有程序文本。
(3)对预定义类型(predefined type)如'object'、'int'、'double'的访问域没有限制。
(4)程序P的顶级类型T,它的访问域定义,按下列步骤:
a.如果P是公共public的,则T的访问域是P和所有引用P的程序的--程序文本;
b.如果P是内部internal的,则T的访问域是P的程序文本;
即,顶级类型T的访问域至少是类型声明所在程序的程序文本。
(5)程序P的类型T嵌套的成员M,它的可访问域定义(M本身也可能是一个类型)按下列步骤:
a.如果M是公共public的,M的访问域是类型T的访问域;
b.如果M是protected internal,D是程序P的程序文本与从T派生的任何类型(在P外声明)的程序文本的合集,M的访问域是T与D可访问域的交集;
c.如果M是protected,D是T的程序文本与派生于T的任意类型的程序文本的合集,M的访问域是T与D访问域的交集;
d.如果M是internal,M的访问域是T的访问域与P程序文本的交集;
e.如果M是private,M的访问域是T的程序文本.
即,可访问M的最小范围是其所在类型的程序文本。
(6)简单说,当类型或成员M是可访问的,按下列步骤,来确保M可访:
a.如果M声明或定义在类型内,而类型是不可访问到的,编译时将汇报错误;
b.而M是公共的,则允许访问;
c.如果M是protected internal,当访问发生在M声明所在的程序内时,或发生在从M声明所在类派生的类中且通过派生类访问时,允许访问;
d.如果M是protected,当访问发生在M声明所在的类内时,或从M声明所在类派生类并通过派生类访问时,允许访问;
e.如果M是internal,当访问发生在M声明所在的程序中时,允许访问;
f.如果M是private,当访问发生在M声明的在的类型中时,允许访问;
g.否则,类型和成员不可访问,编译时报错。
(7)实例成员的受保护访问(protected access for instance members)
a.当protected的实例成员于它的声明所在类的程序文本之外(被)访问时,以及当protected internal实例成员于它的声明所在程序的程序文本之外(被)访问时,要求访问发生在从该成员所在类派生的类中。
b.假设基类B声明了一个protected实例成员M,D从B派生,在D的类体(class-body)内,用以下形式访问M:
~M形式(form M)的非限定类型名(unqualified type-name )或基础表达式(primary-expression);
~E.M基础表达式,只要E是D类型的或从D派生的类型;
~base.M基础表达式;
~在构建函数-初始化设置(constructor-initializer )中,派生类可以访问基类的受保护的实例构建器;
//下例演示了,必须在派生类的实例中访问基类的受保护成员,而不能通过基类的实例来访问(其受保护成员) public class A { protected int x; static void F(A a, B b) { a.x = 1; // Ok b.x = 1; // Ok } } public class B: A { static void F(A a, B b) { a.x = 1; // Error, must access through instance of B b.x = 1; // Ok } }
(8) 可访问性约束(accessibility constraints)
C# 语言中的有些构造要求一个类型至少与某个成员或其他类型具有同样的可访问性。如果 T
的访问域是 M
访问域的超集,我们就说类型 T
至少与成员或类型 M
具有同样的可访问性。换言之,如果 T
在所有可访问 M
的上下文中都是可访问的,则 T
至少与 M
具有同样的可访问性。存在下列可访问性约束:
- 类类型的直接基类,必须至少与类类型本身具有同样的可访问性。
- 接口类型的显式基接口,必须至少与接口类型本身具有同样的可访问性。
- 委托类型的返回类型和参数类型,必须至少与委托类型本身具有同样的可访问性。
- 常数的类型,必须至少与常数本身具有同样的可访问性。
- 字段的类型,必须至少与字段本身具有同样的可访问性。
- 方法的返回类型和参数类型,必须至少与方法本身具有同样的可访问性。
- 属性的类型,必须至少与属性本身具有同样的可访问性。
- 事件的类型,必须至少与事件本身具有同样的可访问性。
- 索引器的类型和参数类型,必须至少与索引器本身具有同样的可访问性。
- 运算符的返回类型和参数类型,必须至少与运算符本身具有同样的可访问性。
- 实例构造函数的参数类型,必须至少与实例构造函数本身具有同样的可访问性。