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
开头后跟句点的每个命名空间声明的命名空间体。 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)具体的某签名是有其适用范围的,由此,也导致了两种隐含:即通过嵌套的隐含和通过继承的隐含。