名稱的作用域(scope)是一個(gè)程序文本區(qū)域(region),在其中可以用過名稱引用實(shí)體聲明而不對該名稱加以限定條件(qualification)。作用域可嵌套,內(nèi)部作用域可重聲明外部作用域名稱的含義(但這并不會移除在第三章第三節(jié)中對其的限制——在嵌套塊內(nèi)不能聲明與閉包塊(enclosing block)內(nèi)局部變量同名的局部變量)。因此可以說此外部作用域的名稱在該內(nèi)部作用域覆蓋的程序文本區(qū)域內(nèi)是隱藏(hidden)的,只能通過限定名來訪問外部名稱。
namespace-member-declaration
(第九章第五節(jié))聲明的命名空間成員的作用域——如果沒有其它 namespace-declaration
對其閉包的話——是整個(gè)(entire)程序文本。namespace-declaration
內(nèi)的 namespace-member-declaration
聲明的命名空間成員的作用域是——如果假設(shè)該命名空間成員聲明的完全限定名為 N
——完全限定名為 N
或以 N
為始、后跟一個(gè)句號(period)的每個(gè) namespace-declaration
的 namespace-body
。extern-alias-directive
定義的名稱的作用域擴(kuò)展到直接包含其編譯單元或命名空間主體的 using-directives
、global-attributes
和 namespace-member-declarations
。extern-alias-directive
并不會為底層聲明空間(underlying declaration space)增加任何成員。換句話說 extern-alias-directive
并不具傳遞性,相反其只會影響到在其內(nèi)出現(xiàn)的 compilation-unit
或 namespace-body
。using-directive
(第九章第四節(jié))定義或?qū)氲拿Q的作用域擴(kuò)展到出現(xiàn) using-directive
的 compilation-unit
或 namespace-body
的整個(gè) namespace-member-declarations
。using-directive
能使零或多個(gè)命名空間或類型名在特定的 compilation-unit
或 namespace-body
變得可用,但并不會為底層聲明空間(underlying declaration space)增加任何成員。換句話說 using-directive
并不具傳遞性,相反其只會影響到在其出現(xiàn)的 compilation-unit
或 namespace-body
。class-declaration
(第十章第一節(jié))中的 type-parameter-list
所聲明的類型形參的作用域是該 class-declaration
的 class-base
、type-parameter-constraints-clauses
以及 class-body
。struct-declaration
(第十一章第一節(jié))中的 type-parameter-list
所聲明的類型形參的作用域是該 struct-declaration
的 struct-interfaces
、type-parameter-constraints-clauses
以及 struct-body
。interface-declaration
(第十三章第一節(jié))中的 type-parameter-list
所聲明的類型形參的作用域是該 interface-declaration
的 interface-base
、type-parameter-constraints-clauses
以及 interface-body
。delegate-declaration
(第十五章第一節(jié))中的 type-parameter-list
所聲明的類型形參的作用域是該 delegate-declaration
的 return-type
、formal-parameter-list
以及 type-parameter-constraints-clauses
。class-member-declaration
(第十章第 1.6 節(jié))所聲明的成員的作用域位于該聲明所出現(xiàn)的 class-body
之內(nèi)。此外,類成員的作用域擴(kuò)展到包含該成員且為可訪問的(accessibility,第三章第 5.2 節(jié))派生類的 class-body
。struct-member-declaration
(第十一章第二節(jié))聲明的成員的作用域位于該聲明出現(xiàn)的 struct-body
之內(nèi)。enum-member-declaration
(第十四章第三節(jié))所聲明的成員的作用域位于該聲明出現(xiàn)的 enum-body
內(nèi)。method-declaration
(第十章第六節(jié))內(nèi)所聲明之參數(shù)的作用域是該 method-declaration
的 method-body
。indexer-declaration
(第十章第九節(jié))內(nèi)所聲明之參數(shù)的作用域是該 indexer-declaration
的 accessor-declarations
。operator-declaration
(第十章第十節(jié))內(nèi)所聲明之參數(shù)的作用域是該 operator-declaration 的 block
。constructor-declaration
(第十章第十一節(jié))內(nèi)所聲明之參數(shù)的作用域是該 constructor-declaration
的 constructor-initializer
和 block
。lambda-declaration
(第七章第十五節(jié))內(nèi)所聲明之參數(shù)的作用域是該 lambda-expression
的 lambda-expression-body
。anonymous-method-expression
(第七章第十五節(jié))內(nèi)所聲明之參數(shù)的作用域是該 anonymous-method-expression
的 block
。labeled-statement
(第八章第四節(jié))內(nèi)所聲明之標(biāo)簽的作用域是該聲明所在的 block
。local-variable-declaration
(第八章第 5.1 節(jié))內(nèi)所聲明之局部變量的作用域是該聲明所在的 block
。switch
語句(第八章第 8.3 節(jié))的 switch-block
內(nèi)所聲明的局部變量的作用域是該 switch-block
。for
語句(第八章第 8.3 節(jié))的 for-initializer
內(nèi)所聲明的局部變量的作用域是該語句的 for-initializer
、for-condition
、for-iterator
以及所含之 statement
。local-constant-declaration
(第八章第 5.2 節(jié))內(nèi)聲明的局部變量的作用域是該聲明出現(xiàn)的 block
。在該局部變量的 constant-declarator
之前的文本位置上引用該局部變量將出現(xiàn)一個(gè)「編譯時(shí)錯誤」。foreach-statement
、using-statement
、lock-statement
或 query-expression
一部分所聲明的變量的作用域由給定構(gòu)造(construct)的擴(kuò)展(expansion)所決定。在命名空間、類、結(jié)構(gòu)或枚舉成員的作用域內(nèi),可以在位于該成員聲明之前的文本位置上引用該成員。比方說
class A
{
void F() {
i = 1;
}
int i = 0;
}
在此,F(xiàn) 在 i 聲明之前引用它是合法的。
在局部變量的作用域內(nèi),當(dāng)引用局部變量的文本位置(textual position)在該局部變量聲明之前(local-variable-declarator)會出現(xiàn)一個(gè)「編譯時(shí)錯誤」。比方說
class A
{
int i = 0;
void F() {
i = 1; // 錯誤,在使用前先聲明
int i;
i = 2;
}
void G() {
int j = (j = 1); // 有效
}
void H() {
int a = 1, b = ++a; // 有效
}
}
在上面方法 F
中,第一個(gè)對 i
的賦值實(shí)際上并不會引用外部作用域的字段。相反,它引用了本地局部變量,而其結(jié)果是引發(fā)一個(gè)「編譯時(shí)錯誤」,因?yàn)樵谖谋旧弦笙嚷暶髯兞?。在方?G
中,為 j
進(jìn)行聲明的同時(shí)在其初始化器內(nèi)使用 j
是有效的,這是因?yàn)椴]有在 local-variable-declarator
之前使用。在方法 H
中,后面的 local-variable-declarator
正確引用了局部變量(該局部變量在同一個(gè) local-variable-declarator
內(nèi)前面那個(gè) local-variable-declarator
中聲明了)。
本地布局變量的作用域規(guī)則被設(shè)計(jì)為保證(guarantee)表達(dá)式上下文(expression context)中使用的名稱的含義與在塊中使用的含義是相同的。如果局部變量的作用域只從其聲明之處其,到塊尾部截止,那么在上例中,第一個(gè)賦值將分配給實(shí)例變量(instance variable),第二次賦值將分配給局部變量,如果塊中的語句之后重新排列,會引發(fā)「編譯時(shí)錯誤」。
塊(block)中名稱的含義可能因名稱的使用上下文的不同而有所不同。在下例中,
using System;
class A {}
class Test
{
static void Main() {
string A = "hello, world";
string s = A; // 表達(dá)式上下文
Type t = typeof(A); // 類型上下文
Console.WriteLine(s); // 輸出 "hello, world"
Console.WriteLine(t); // 輸出 "A"
}
}
在表達(dá)式上下文中的名稱 A
引用本地變量 A
,在類型上下文中引用類型 A
。
實(shí)體(entity)的作用域(scope)往往比實(shí)體聲明空間包含(encompasses)更多的程序文本。具體來說,實(shí)體的作用域可能會引入新的聲明空間,而其中或許會包含與該實(shí)體同名的實(shí)體。這類聲明導(dǎo)致原始實(shí)體變?yōu)?strong>隱藏的(hidden)。相反,如果實(shí)體沒有被隱藏,那么我們說它是可見的(visible)。
當(dāng)作用域通過嵌套交叉(overlap)或當(dāng)作用域通過繼承交叉,那么會導(dǎo)致名稱隱藏。兩個(gè)被隱藏的類型的特征在下面兩節(jié)中進(jìn)行介紹。
在命名空間內(nèi)嵌套命名空間或類型,或者在類或結(jié)構(gòu)內(nèi)嵌套類型,以及性參與局部變量的聲明,都將導(dǎo)致嵌套隱藏(hiding through nesting)名稱。舉個(gè)例子。
class A
{
int i = 0;
void F() {
int i = 1;
}
void G() {
i = 1;
}
}
在方法 F
內(nèi),實(shí)例變量 i
被局部變量 i
所隱藏,但在方法 G
內(nèi),i
依舊引用實(shí)例變量。
當(dāng)內(nèi)部作用域的名稱隱藏了外部作用域的名稱時(shí),它將隱藏該名稱的所有重載。下例中,
class Outer
{
static void F(int i) {}
static void F(string s) {}
class Inner
{
void G() {
F(1); // 調(diào)用 Outer.Inner.F
F("Hello"); // 錯誤
}
static void F(long l) {}
}
}
調(diào)用(call)F(1)
將調(diào)用(invokes)內(nèi)部聲明的 F
,這是因?yàn)樗型獠砍霈F(xiàn)的 F
都被內(nèi)部聲明所隱藏。同樣的,調(diào)用 F("Hello")
的結(jié)果是出現(xiàn)「編譯時(shí)錯誤」。
當(dāng)類或結(jié)構(gòu)重新聲明(redeclare)從基類繼承的名稱時(shí),會發(fā)生通過繼承隱藏(hiding through inheritance)其名稱。這種類型的名稱隱藏采取以下形式:
管理運(yùn)算符聲明(governing operator declarations,第十章第十節(jié))的規(guī)則使其不可能在派生類中聲明一個(gè)與其基類內(nèi)同簽名的運(yùn)算符。因此,操作符不能相互隱藏。
與從作用域外部對名稱進(jìn)行隱藏相反,通過繼承的作用域隱藏名稱可訪問性會報(bào)出警告。下例中,
class Base
{
public void F() {}
}
class Derived: Base
{
public void F() {} // 警告,隱藏了繼承到的名稱
}
派生類 Derived
中 F
的聲明會導(dǎo)致一個(gè)警告。隱藏所繼承的名稱實(shí)際上不是一個(gè)錯誤,因?yàn)檫@會妨礙基類自身的單獨(dú)改進(jìn)。比方說,上述情況可能會發(fā)生因?yàn)?Base
的后續(xù)版本可能會引入一個(gè) F
方法(而這個(gè)方法在之前版本中沒有)。如果上述情況是錯誤的,那么對單獨(dú)版本控制的基類的任何變化都會潛在導(dǎo)致派生類變得無效。
因隱藏繼承到的名字而引發(fā)的警告可通過 new
修飾符消除:
class Base
{
public void F() {}
}
class Derived: Base
{
new public void F() {}
}
修飾符 new
表示派生類 Derived
中的 F
是「新的」,它將隱藏所繼承到的(同簽名)成員。新成員的聲明將隱藏其所繼承到的,當(dāng)且僅當(dāng)位于新成員的作用域內(nèi)。
class Base
{
public static void F() {}
}
class Derived: Base
{
new private static void F() {} // 只會隱藏 Derived 內(nèi)的 Base.F
}
class MoreDerived: Derived
{
static void G() { F(); } // 調(diào)用 Base.F
}
上例中,派生類中的 F 聲明隱藏了(hide)其從基類繼承到的 F。但由于派生類中新 F 的可訪問性是 private
,因此它的作用域達(dá)不到 MoreDerived
。因此 MoreDerived.G
調(diào)用(call) F()
方法依舊合法,它將調(diào)用 Base.F
。
更多建議: