C#-基本概念

应用程序启动

具有入口点 (entry point) 的程序集称为应用程序 (application)。应用程序运行时,将创建新的应用程序域 (application domain)。同一台计算机上可能会同时运行着同一个应用程序的若干个实例,此时,每一个实例都拥有各自的应用程序域。
应用程序域用作应用程序状态的容器,以此隔离应用程序。应用程序域作为应用程序中和它使用的类库中所定义的类型的容器和边界。同一个类型若被加载到不同的应用程序域中就成为各自独立的客体,由它们在各自应用程序域中产生的实例亦不可直接共享。例如,对于这些类型的静态变量,每个应用程序域都有自己的副本,并且这些类型的静态构造函数在每个应用程序域中也要(最多)运行一次。关于如何处理程序域的创建和销毁,各实现可以按具体情况确定自己的策略或机制。
当执行环境调用指定的方法(称为应用程序的入口点)时发生应用程序启动 (application startup)。此入口点方法总是被命名为 Main,可以具有下列签名之一:

static void Main() {
   ...}
static void Main(string[] args) {
   ...}
static int Main() {
   ...}
static int Main(string[] args) {
   ...}

如上所示,入口点可以选择返回一个 int 值。此返回值用于应用程序终止。
入口点可以包含一个形参(可选)。该参数可以具有任意名称,但参数的类型必须为 string[]。如果存在形参,执行环境会创建并传递一个包含命令行实参的 string[] 实参,这些命令行实参是在启动应用程序时指定的。string[] 参数永远不能为 null,但如果没有指定命令行参数,它的长度可以为零。
由于 C# 支持方法重载,因此类或结构可以包含某个方法的多个定义(前提是每个定义有不同的签名)。但在一个程序内,没有任何类或结构可以包含一个以上的名为 Main 的方法,因为 Main 的定义限定它只能被用作应用程序的入口点。允许使用 Main 的其他重载版本,前提是它们具有一个以上的参数,或者它们的唯一参数的类型不是 string[]。
应用程序可由多个类或结构组成。在这些类或结构中,可能会有若干个拥有自己的 Main 方法,因为 Main 的定义限定它只能被用作应用程序的入口点。这样的情况下,必须利用某种外部机制(如命令行编译器的选项)来选择其中一个 Main 方法用作入口点。
在 C# 中,每个方法都必须定义为类或结构的成员。通常,方法的已声明可访问性由其声明中指定的访问修饰符确定。同样,类型的已声明可访问性由其声明中指定的访问修饰符确定。为了能够调用给定类型的给定方法,类型和成员都必须是可访问的。然而,应用程序入口点是一种特殊情况。具体而言,执行环境可以访问应用程序的入口点,无论它本身的可访问性和封闭它的类型的可访问性是如何在声明语句中设置的。
应用程序入口点方法不能位于泛型类声明中。
在所有其他方面,入口点方法的行为与非入口点方法类似。

应用程序终止

应用程序终止 (application termination) 将控制返回给执行环境。
如果应用程序的入口点 (entry point) 方法的返回类型为 int,则返回的值用作应用程序的终止状态代码 (termination status code)。此代码的用途是允许与执行环境进行关于应用程序运行状态(成功或失败)的通信。
如果入口点方法的返回类型为 void,那么在到达终止该方法的右大括号 (}),或者执行不带表达式的 return 语句时,将产生终止状态代码 0。
在应用程序终止之前,将调用其中还没有被垃圾回收的所有对象的析构函数,除非已将这类清理功能设置为取消使用(例如,通过调用库方法 GC.SuppressFinalize)。

声明

C# 程序中的声明定义程序的构成元素。C# 程序是用命名空间组织起来的,一个命名空间可以包含类型声明和嵌套的命名空间声明。类型声明用于定义类、结构、接口、枚举和委托。在一个类型声明中可以使用哪些类型作为其成员,取决于该类型声明的形式。例如,类声明可以包含常量声明、字段声明、方法声明、属性声明、事件声明、索引器声明、运算符声明、实例构造函数声明、静态构造函数声明、析构函数声明和嵌套类型声明。
一个声明在它自已所属的那个声明空间 (declaration space) 中定义一个名称。除非是重载成员,否则,在同一个声明空间下若有两个以上的声明语句声明了具有相同名称的成员,就会产生编译时错误。同一个声明空间内绝不能包含不同类型的同名成员。例如,声明空间绝不能包含同名的字段和方法。
有若干种不同类型的声明空间,如下所述。

  • 在程序的所有源文件中,namespace-member-declaration 若没有被置于任何一个 namespace-declaration 下,则属于一个称为全局声明空间 (global declaration space) 的组合声明空间。
  • 在程序的所有源文件中,一个 namespace-member-declaration 若在 namespace-declaration 中具有相同的完全限定的命名空间名称,它就属于一个组合声明空间。
  • 每个类、结构或接口声明创建一个新的声明空间。名称将通过 class-member-declaration、struct-member-declaration、interface-member-declaration 或 type-parameter 引入此声明空间。除了重载实例构造函数声明和静态构造函数声明外,类或结构不能包含与该类或结构同名的成员声明。类、结构或接口允许声明重载方法和索引器。另外,类或结构允许重载实例构造函数和运算符的声明。例如,类、结构或接口可以包含多个同名的方法声明,前提是这些方法声明的签名不同。注意,基类与类的声明空间无关,基接口与接口的声明空间无关。因此,允许在派生类或接口内声明与所继承的成员同名的成员。我们说这类成员隐藏 (hide) 了它们继承的那些成员。
  • 每个委托声明创建一个新的声明空间。名称通过形参(fixed-parameter 和 parameter-array)和 type-parameter 引入此声明空间。
  • 每个枚举声明创建一个新的声明空间。名称通过 enum-member-declarations 引入此声明空间。
  • 每个方法声明、索引器声明、运算符声明、实例构造函数声明和匿名函数均创建一个称为局部变量声明空间 (local variable declaration space) 的新声明空间。名称将通过形参(fixed-parameter 和 parameter-array)和 type-parameter 引入此声明空间。函数成员或匿名函数的主体(如果有)将视为嵌套在局部变量声明空间中。如果局部变量声明空间和嵌套的局部变量声明空间包含具有相同名称的元素,则会发生错误。因此,在嵌套声明空间中不可能声明与封闭它的声明空间中的局部变量或常量同名的局部变量或常量。只要两个声明空间彼此互不包含,这两个声明空间就可以包含同名的元素。
  • 每个 block 或 switch-block 以及 for、foreach 和 using 语句都会为局部变量和局部常量创建一个局部变量声明空间。名称将通过 local-variable-declaration 和 local-constant-declaration 引入此声明空间。请注意,作为函数成员的主体或匿名函数的主体出现或出现在该主体之中的块将嵌套在这些函数为其参数声明的局部变量声明空间中。因此,如果某个方法的局部变量和参数具有相同名称,则会发生错误。
  • 每个 block 或 switch-block 都为标签创建一个单独的声明空间。名称将通过 labeled-statement 引入此声明空间,并通过 goto-statement 进行引用。块的标签声明空间可包含任何嵌套块。因此,在嵌套块中不可能声明与封闭它的块中的标签同名的标签。
    声明名称的文本顺序通常不重要。具体而言,声明和使用命名空间、常量、方法、属性、事件、索引器、运算符、实例构造函数、析构函数、静态构造函数和类型时,文本顺序并不重要。在下列情况下声明顺序非常重要:
  • 字段声明和局部变量声明的声明顺序确定其初始值设定项(如果有)的执行顺序。
  • 在使用局部变量前必须先定义它们。
  • 当省略 constant-expression 值时,枚举成员声明的声明顺序非常重要。
    命名空间的声明空间是“开放式的”,两个具有相同的完全限定名的命名空间声明提供相同的声明空间。
    例如:
namespace Megacorp.Data
{
   
     class Customer
     {
   
          ...
     }
}

namespace Megacorp.Data
{
   
     class Order
     {
   
          ...
     }
}

上面的两个命名空间声明为同一声明空间提供了成员,在本例中它们分别声明了具有完全限定名 Megacorp.Data.Customer 和 Megacorp.Data.Order 的两个类。由于两个声明共同构成同一个声明空间,因此如果每个声明中都包含一个同名类的声明,则将导致编译时错误。
正如上面所述,块的声明空间包括所有嵌套块。因此,在下面的示例中,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();
     }
}

成员

命名空间和类型具有成员 (member)。通常可以通过限定名来访问实体的成员。限定名以对实体的引用开头,后跟一个“.”标记,再接成员的名称。
类型的成员或者是在该类型声明中声明的,或者是从该类型的基类继承 (inherit) 的。当类型从基类继承时,基类的所有成员(实例构造函数、析构函数和静态构造函数除外)都成为派生类型的成员。基类成员的声明可访问性并不控制该成员是否可继承:继承性可扩展到任何成员,只要它们不是实例构造函数、静态构造函数或析构函数。然而,在派生类型中可能不能访问已被继承的成员,原因或者是因为其已声明可访问性,或者是因为它已被类型本身中的声明所隐藏。

1> 命名空间成员

命名空间和类型若没有封闭它的命名空间,则属于全局命名空间 (global namespace) 的成员。这直接对应于全局声明空间中声明的名称。
在某命名空间中声明的命名空间和类型是该命名空间的成员。这直接对应于该命名空间的声明空间中声明的名称。
命名空间没有访问限制。不可能把命名空间设置成私有的、受保护的或内部的,命名空间名称始终是可公开访问的。

2> 结构成员

结构的成员是在结构中声明的成员以及继承自结构的直接基类 System.ValueType 和间接基类 object 的成员。
简单类型的成员直接对应于结构类型的成员,此简单类型正是该结构的化名:

  • 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.Decimal 结构的成员。
  • bool 的成员是 System.Boolean 结构的成员。

3> 枚举成员

枚举的成员是在枚举中声明的常量以及继承自枚举的直接基类 System.Enum 和间接基类 System.ValueType 和 object 的成员。

4> 类成员

类的成员是在类中声明的成员和从该类的基类(没有基类的 object 类除外)继承的成员。从基类继承的成员包括基类的常量、字段、方法、属性、事件、索引器、运算符和类型,但不包括基类的实例构造函数、析构函数和静态构造函数。基类成员被是否继承与它们的可访问性无关。
类声明可以包含以下对象的声明:常量、字段、方法、属性、事件、索引器、运算符、实例构造函数、析构函数、静态构造函数和类型。
object 和 string 的成员直接对应于它们所化名的类类型的成员:
<1> object 的成员是 System.Object 类的成员。
<2> string 的成员是 System.String 类的成员。

5> 接口成员

接口的成员是在接口中和该接口的所有基接口中声明的成员。严格地说,类 object 中的成员不是任何接口的成员。但是,通过在任何接口类型中进行成员查找,可获得类 object 中的成员。

6> 数组成员

数组的成员是从类 System.Array 继承的成员。

7> 委托成员

委托的成员是从类 System.Delegate 继承的成员。

成员访问

成员的声明可用于控制对该成员的访问。成员的可访问性是由该成员的声明可访问性和直接包含它的那个类型的可访问性(若它存在)结合起来确定的。
如果允许访问特定成员,则称该成员是可访问的 (accessible)。相反,如果不允许访问特定成员,则称该成员是不可访问的 (inaccessible)。当引发访问的源代码的文本位置在某成员的可访问域中时,允许对该成员进行访问。

1> 已声明可访问性

成员的已声明可访问性 (declared accessibility) 可以是下列类型之一:

  • Public,选择它的方法是在成员声明中包括 public 修饰符。public 的直观含义是“访问不受限制”。
  • Protected,选择它的方法是在成员声明中包括 protected 修饰符。protected 的直观含义是“访问范围限定于它所属的类或从该类派生的类型”。
  • Internal,选择它的方法是在成员声明中包括 internal 修饰符。internal 的直观含义是“访问范围限定于此程序”。
  • Protected internal(意为受保护或内部的),选择它的方法是在成员声明中包括 protected 和 internal 修饰符。protected internal 的直观含义是“访问范围限定于此程序或那些由它所属的类派生的类型”。
  • Private,选择它的方法是在成员声明中包括 private 修饰符。private 的直观含义是“访问范围限定于它所属的类型”。
    声明一个成员时所能选择的已声明可访问性的类型,依赖于该成员声明出现处的上下文。此外,当成员声明不包含任何访问修饰符时&#
  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值