effective hierarchy(一)之 基本概念(3)

MSDN,C#2.0:

 

一、签名与重载(signatures and overloading)

 

1.签名用于描述方法、实例构造函数、索引器和操作符的特征(characterized)。作为特征描述的签名中的每个形参都是有顺序的,基本是从左到右

 

 (1)方法签名:

            包含方法名称及每个(each of)形参的类型(type)和种类(kind-value、reference or output),不包括返回类型和params修饰符(用于最右侧的参数)。

 (2)实例构造函数签名:

            包含每个形参的类型和种类(value、reference or output),但不包括params修饰符(用于最右侧的参数)。即与params修饰符无关。

 (3)索引器签名:

            包含每个形参的类型,但不包括元素类型(element type)。即元素类型不同,但认为签名相同;

 (4)操作符签名:

            包含操作符名称和每个形参的类型,但不包括结果类型(result type)。

 

2.签名使在类、结构、接口中进行成员重载(overloading of members )成为现实(enabling mechanism )

 

 (1)方法重载:

           允许类、结构、接口(简称CSI)定义若干同名的方法,前提是签名在CSI中唯一;

 (2)实例构建方法重载:

           允许类、结构(简称CS)定义若干实例构建方法,前提是签名在CS中唯一;

 (3)索引器重载:

           允许CSI定义若干索引器,前提是签名在CSI中唯一;

 (4)操作符重载:

           允许CS定义若干同名的操作符,前提是签名在CS中唯一。

           注意:任何ref和out这样的参数修饰符(parameter modifier),都是签名的一部分。

   

//方法重载的接口示例,注意标明error的地方
interface ITest
{
   void F();                     // F()
   void F(int x);                  // F(int)
   void F(ref int x);            // F(ref int)
   void F(int x, int y);         // F(int, int)
   int F(string s);               // F(string)
   int F(int x);                  // F(int)         error
   void F(string[] a);            // F(string[])
   void F(params string[] a);      // F(string[])      error
}

 

二、范围(scope) ,或生效范围

 

1.名称的范围是一个程序文本区域,在此范围内,可以引用(refer to)由该名称声明的实体,无需名称限定(without qualification of the name);

 

2.范围可以嵌套。内部范围可以重新确定外部范围中的名称含义(但下列限制仍然有效:即,在嵌套块中不可能声明与它的封闭块中的局部变量同名的局部变量);这时的外部范围的名称,被称做是在内部范围覆盖(covered)的程序文本区域中隐含,并且只能通过限定名称来访问外部(范围)的名字;注,这里的外部是指嵌套关系的上一层级.

 

3.具体范围:

  • 一个由“命名空间成员声明”所声明的命名空间成员的范围,如果没有其他封闭它的“命名空间成员声明”,则它的范围是整个程序文本。
  • 命名空间声明中命名空间成员声明所声明的命名空间成员的范围是这样定义的,如果该命名空间成员声明的完全限定名为 N,则其声明的命名空间成员的范围是,完全限定名为 N 或以 N 开头后跟句点的每个命名空间声明的命名空间体。
  • 由 using 指令定义或导入的名称的是,出现 using 指令的编译单元或命名空间体内的所有的整个“命名空间成员声明”中。using 指令可以使零个或更多的命名空间或者类型名称在特定的编译单元或命名空间体中可用,但不会把任何新成员提供给隐含的下层声明空间。换言之,using 指令仅在使用它的编译单元或命名空间体范围内有效,它的功效是不可传递的。
  • 由“类成员声明”所声明的成员范围,是该声明所在的那个“类体”。此外,类成员的范围扩展到该成员的可访问域中的那些派生类的“类体”。
  • 由“结构成员声明”声明的成员范围,是该声明所在的结构体(struct-body.‘体'在以下是指-body)。
  • 由“枚举成员声明”声明的成员范围,是该声明所在的枚举体。
  • 在“方法声明”中声明的参数范围,是该方法所在的方法体。
  • 在“索引器声明”中声明的参数范围,是该索引器声明的访问器声明。
  • 在“运算符声明”中声明的参数范围,是该运算符声明所在的块。
  • 在“构造函数声明”中声明的参数范围,是该构造函数声明的构造函数初始值设定项和块。
  • 在“标记语句”中声明的标签范围,是该声明所在的块。
  • 在“局部变量声明”中声明的局部变量范围,是该声明所在的块。
  • switch 语句的 switch 块中声明的局部变量范围,是 switch 块。
  • for 语句的 for 初始值设定项中声明的局部变量的范围,是该 for 语句的 for 初始值设定项、for 条件、for 迭代程序以及所包含的语句。
  • 在局部常量声明中声明的局部常量范围,是该声明发生的块。在某局部常量常数声明符之前的文本位置中引用该局部常量是编译时错误。

4.使用

 (1)在名字空间、类、结构或枚举成员的范围内,允许在成员声明之前的文本位置引用该成员

        

//以下程序文本是合法的
class A
{
   void F() {
      i = 1;
   }
   int i = 0;
}

 

 (2)在本地变量范围内,先引用后声明,发生编译时错误

   

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

//想想,为什么i=1是错误的...因为i在函数F的内部范围内重新定义了

 

 (3)本地变量的范围规则(scoping rules)用于保证:

           在表达式环境(context)中名称的含义,与包含它的块内的其它使用,在意义上保持一致。如果局部变量的范围仅是要(were to extend)从它的声明扩展到块的结尾,则上面(2)中的示例,第一次赋值将会分配给实例变量,第二次赋值将会分配给局部变量。如果后来重排(rearrange)块的语句,有可能会导致编译时错误,故不建议这样使用。

 

 (4)块中名称的含义基于(based on)名称使用的上下文(context),从而有可能产生差异性(differ)。

//一个是类型A,一个是本地变量A,如下 
using System;
class A {}
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);               // writes "A"
   }
}

 

三、名称隐含(name hiding)

 

1.实体范围(scope of entity)包含的(encompass)程序文本通常比该实体的声明空间( declaration space )要多。特别是实体范围有可能引入包含同名实体的新的声明空间。这种使用会导致原始(original)实体变为隐式实体。反之,如果实体不是“隐含”(hidden),则一定“可见”(visible)。

 

2.当范围交叉时(scope overlap)发生名称隐藏。一般存在两种情况,即通过嵌套(nesting)或通过继承(inheritance)发生。

 

 (1)通过嵌套(发生)的名称隐藏(hiding throught nesting),是发生下述三种情况的结果:

         ~在名字空间内嵌套名字空间或类型的结果;

         ~在类或结构中嵌套类型的结果;

         ~参数和本地变量声明的结果。

   

//示例:在F中,实例变量i被本地变量i所隐藏,但在G中,i仍引用实例变量
class A
{
   int i = 0;
   void F() {
      int i = 1;
   }
   void G() {
      i = 1;
   }
}

 

 (2)注意:当内部范围中的名称隐藏外部范围中的名称时,它隐藏该名称的所有重载的发生(overloaded occurrence)

   

//以下示例中,当调用内部的F(1)后,再调用外部的F("hello"),编译器不能通过

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) {} //这里可以看到在类体内,成员出现顺序具无关性.
   }
}

   

 

 (3) 通过继承(发生)的名称隐藏(hiding through inheritance),发生于类或结构对继承于某基类的名称进行重新声明(redeclare)时,它使用以下形式:

 

  a.在类或结构(简称CS)中引入的(introduced)常量、字段、属性、事件或类型,隐藏所有的同名基类成员;

 

   b.在CS中引入方法时,隐藏所有同名的非方法基类成员和所有具相同签名(方法名、参数量、修饰符和类型)的基类方法;

     c.在CS中引入索引器时,隐藏所有具相同签名的基类索引器(参数量、类型)。

 

 (4)操作符(operator)声明的管理规则,使从派生类声明的操作符不会与基类中的操作符具有相同签名。正因如此,操作符从不发生隐含

 

 (5)与隐藏外部范围的名称相反,从一个继承的范围(inherited scope)隐藏某可访问的名称,会产生一个警告。通过使用New修饰符可以消除这种警告:它的含义是派生的F是“新的”,并且是确实要隐藏继承的成员;

        

//按“常规”进行继承的隐含,会出现错误
class Base
{
   public void F() {}
}
class Derived: Base
{
   public void F() {}      // Warning, hiding an inherited name
}

//使用new修饰符,做继承范围的真正隐含,并消除警告!
class Base
{
   public void F() {}
}
class Derived: Base
{
   new public void F() {} //注意new
}

 

 

 (6)新成员(new member)的声明,只在新成员范围内隐含其所继承的成员(inherited member);

        

//用途示例
class Base
{
   public static void F() {}
}

class Derived: Base
{
   new private static void F() {}   // 只在派生类的范围隐含Base.F
}

class MoreDerived: Derived
{
   static void G() { F(); }         // 这里是调用Base.F
}

 

 

小结:

(1)签名用来明确描述成员,包括名称、类型和各类(数值还是引用);

(2)具体的某签名是有其适用范围的,由此,也导致了两种隐含:即通过嵌套的隐含和通过继承的隐含。

你可能感兴趣的:(F#,J#)