C#专题-C#的签名和重载,范围

来源:互联网 发布:ae auto sway mac 编辑:程序博客网 时间:2024/06/03 12:33
上一讲,我们学习了C#成员的定义和成员的应用,这一节,我们来一起学习C#的签名和重载,及C#里名字的范围

一、C#的签名和重载
方法、构造函数、标签和操作符按照它们的签名分类:
一个的签名由方法和对象的名称、修饰符和它的形参的类型组成。方法的签名中不包括返回类型。
一个函数的签名由数字修饰符和它的形参的类型组成。
一个标签的签名是由数字和它的形参的类型组成。标签的签名不包括元素类型。
操作符的签名由操作符的名称和数字还有它的形参的类型组成。操作符的签名不包括结果类型。

签名激活了类、结构和接口中成员的重载机制:
方法的重载允许类、结构或接口用相同的名称声明多个方法,并且所提供的方法的签名都是唯一的。
构造函数的重载允许一个类或结构声明多个构造函数,所提供的构造函数的签名都是唯一的。
标签的重载允许类、结构或接口声明多个标签,所提供的标签的签名都是唯一的。
操作符的重载允许一个类或结构用相同的名称声明多个操作符,所提供的操作符的签名都是唯一的。

下面的例子介绍了一系列方法声明重载和它们的签名。
      interface ITest
      {
         void F( );             // F()
         void F(int x);        // F(int)
        void F(ref int x);     // F(ref int)
        void F(out int x);     // F(out int)
        void F(int x, int y);  // F(int, int)
         int F(string s);      // F(string)
         int F(int x);         // F(int)
      }
    我们看到,参数修饰符是签名的一部分。这样F(int)、F(ref int)和F(out int)都是唯一的签名。此外注意:第二个和最后一个方法的声明的返回类型,它们的签名都是F(int)。这样,在编译上面的例子时会在第二个和最后一个例子产生错误。

-------思多雅[天行健]版权所有,首发太平洋论论坛,转载请注明-------

二、名字的范围
2.1范围
一个名字的范围就是程序文本的区域,在这个范围内可以通过声明的名称来查找实体,名称没用条件限制。范围可以嵌套,并且内层的范围可以对从外层范围来的名称的意义进行再次声明。从外层范围来的名称称为在内层范围覆盖的程序文本区域内被隐藏了,并且对外层名称的访问只能通过限定名称。
在被不包括嵌套名称空间声明的名称空间成员声明中表示的名称空间成员的范围对于每个编译单元的程序文本来说是完整的。
在一个全称为N 的名称空间空间声明中由名称空间成员声明表示的名称空间成员的范围是任何一个全称为N 或由与N 有相同的修饰符序列的名称空间声明的名称空间结构体。
通过使用指示符定义或引入名称的范围扩展到使用指示符发生的编译单元或名称空间结构体的名称空间成员声明中。一个使用指示符可能会使得在某个编译单元或名称空间结构体中得到从0 到多个名称空间或类型名称,但是并不把任何新成员放到主要声明域中。换句话说,使用指示符并没有传递,而只是影响它出现的编译单元或名称空间结构体。
在类成员声明中表示的成员的范围是表示所在的类结构体。另外,一个类成员的范围扩展到派生类的类结构体,这些派生类包含在成员的可访问性域 (§3.3.2)中。
在结构成员声明中表示的成员的范围表示所在的结构体。
在枚举成员声明中表示的成员的范围是声明所在的枚举结构体。
在构造函数声明中表示的参数的范围,是这个构造函数声明的构造函数初始化程序和块。
在方法声明中表示的参数的范围是这个方法声明的方法结构体。
在标签声明中表示的参数的范围是这个标签声明的访问声明。
在操作符声明中表示的参数的范围是这个操作符声明的块。
在局部变量声明中表示的局部变量的范围是声明出现的块。从执行局部变量的变量声明的文本位置引用局部变量是错误的。
在for 声明的for 初始化程序中表示的局部变量的范围是for 声明的for 初始化、for 条件、for {循环指示符)和所包含的声明。
在标签声明中表示的标签的范围是表示所在的块。

     在名称空间、类、结构或枚举成员的范围中,可以把成员引用到执行对对象的声明的文字位置。例如
       class A
       {
          void F() {
             i = 1;
           }
          int i = 0;
       }
这里,对于F 来说在没有被表示前把I 引入是有效的。
    在一个局部变量的范围中,在执行局部变量变量说明符的文本位置引入局部变量是错误的。例如
       class A
       {
          int i = 0;

          void F( )  {
             i = 1;                 // Error, use precedes declaration
             int i;
             i = 2;
           }
          void G( )  {
             int j = (j = 1);       // Legal
           }
          void H( )  {
             int a = 1, b = ++a;  // Legal
           }
       }

    在上面的方法F 中,第一次对I 赋值很明确没有引用在外层范围声明的域。它指向局部变量而且它是错误的,因为它从文字上在变量的声明之前。在方法G 里,在初始化程序中使用j 来声明是合法的,因为使用并没有在变量声明之前。在方法H 中,一个后来的变量声明合法的引用了一个局部变量,这个局部变量在早先的变量声明时声明过了。
    对于局部变量的确定范围的规则是要保证用于表达式中的名称的意义与块中的相同。如果局部变量的范围只是从他的声明延伸到块结束的地方,那么就像上面的例子中所示,第一个赋值将赋给实例变量,而第二个赋值将赋给局部变量,如果块的声明在以后被重新分配,就有可能导致错误。
    在块内的名称的意义可能会根据使用名称的上下文有所不同。在例子中
class Test
       {
           static void Main( )  {
              string A = "hello, world";
              string s = A;                             // expression context
              Type t = typeof(A);                       // type context

              Console.WriteLine(s);                     // writes "hello, world"
              Console.WriteLine(t.ToString());           // writes "Type: A"
           }
       }

    名称A 用在一个表达式中来引用局部变量A,而在一个类型中引用类A。

2.2 关于名称隐藏

    典型情况下,一个实体的范围比实体本身的声明域要涉及更多的程序文字。特别是,一个实体的范围可能会包括声明,这些声明会引入一些包含有相同名称的实体的新声明域。这样的声明会使原来的实体变为隐藏。相反,当一个实体不是隐藏的时候,称为可视。
当嵌套引起范围重叠时和当继承引起范围重叠时,就会出现名称隐藏。两种隐藏的特征在下面的章节中描述。

2.2.1 通过嵌套隐藏
    出现在当名称空间中的名称空间和类型的嵌套,或者类和结构中的类型嵌套,或者是参数和局部变量声明的都会造成通过嵌套隐藏名称的出现。通过嵌套隐藏名称通常是“安静地”发生,也就是当外部名称被内部名称隐藏起来时,不会报告错误和警告。
    在这个例子中
       class A
       {
           int i = 0;
          void F() {
              int i = 1;
           }
          void G( )  {
              i = 1;
           }
       }

    在方法F 中,实例变量I 被局部变量I 隐藏了,但是在方法G 中,I 还是引用实例变量。
    当一个内部范围的名称隐藏一个外部范围的名称,它会把那个名称的所有重载都隐藏。在例子中
       class Outer
       {
           static void F(int i) { }
           static void F(string s) { }
           class Inner
           {
              void G( ) {
                 F(1);             // Invokes Outer.Inner.F
                 F("Hello");        // Error
              }
      static void F(long l) {}
          }
      }
    对F(1)的调用实际调用了在内层声明的F,因为所有F 的外部事件都被内部声明隐藏了。出于相同的原因,调用F("Hello")是错误的。

2.2..2 通过继承隐藏

名称通过继承隐藏在类或结构重新对从基类继承来的名称声明时发生。这种类型的名称隐藏有下面形式中的一种:
一个引入类或结构的常数、域、属性、事件或类型隐藏了所有基类中名称相同的成员。
一个引入类或结构的方法隐藏了所有有相同名称的非方法基类成员和所有有相同签名的基类成员(方法名称和参数数目、修饰符和类型)。
一个引入类或结构的索引隐藏所有有相同签名的基类索引(参数数目和类型)。

    管理操作符声明的规则使得一个派生类可以用与基类中的一个操作符相同的签名声明一个操作符。这样,操作符就不会隐藏其它的操作符。
    与隐藏外部范围的名称相反,隐藏继承范围的可访问名称就会报告警告。在例子中
      class Base
      {
         public void F( ) { }
      }
      class Derived: Base
      {
         public void F( ) { }       // Warning, hiding an inherited name
      }

    在Derived 中F 的声明产生了一个警告报告。隐藏一个继承的名称肯定不是错误,因为这样将排除基类单独更新。例如,因为Base 后来的版本把不在先前版本类中的F 方法引入,这样就会产生上面的情况。如果上面的情况是一个错误,那么任何对于在独立版本类库中的基类的变更就会潜在地使得派生类变为无效。
    由于继承名称引起的警告可以通过使用新修饰符来消除:
      class Base
      {
         public void F( ) { }
      }
      class Derived: Base
      {
         new public void F( ) { }
      }

    新的修饰符指出Derived 中的F 是“新”,并且它实际上是要隐藏继承成员。
    新成员的声明只是在新成员的范围内隐藏继承的成员。
      class Base
      {
         public static void F( ) { }
      }
  class Derived: Base
      {
         new private static void F( ) { }  // Hides Base.F in Derived only
      }
      class MoreDerived: Derived
      {
         static void G( ) { F( ); }        // Invokes Base.F
      }
   在上面的例子中,Derived 中F 的声明隐藏了从Base 继承的F,但是由于Derived 中的新F 有私有的可访问性,所以它的范围不会扩展到MoreDerived。这样,MoreDerived.G 中对F()的调用是有效的,并且会引用Base.F。
原创粉丝点击