C# 成員訪問(wèn)

2018-09-28 18:41 更新

成員訪問(wèn)

成員聲明可用于控制對(duì)其的訪問(wèn)。成員可訪問(wèn)性由該成員的聲明的可訪問(wèn)性(declared accessibility,第三章第 5.1 節(jié))以及包含該成員的類型的可訪問(wèn)性(如果存在的話)來(lái)確定的。

當(dāng)允許訪問(wèn)指定成員時(shí),我們稱該成員是可訪問(wèn)的(accessible)。相反,當(dāng)不允許訪問(wèn)指定成員時(shí),我們稱該成員是不可訪問(wèn)的(inaccessible)。

當(dāng)引發(fā)訪問(wèn)的文本位于成員的可訪問(wèn)域(accessibility domain,第三章第 5.2 節(jié))內(nèi),則該成員允許被訪問(wèn)。

聲明的可訪問(wèn)性

成員的聲明可訪問(wèn)性(declared accessibility)可為以下類型之一:

  • Public,通過(guò)在成員聲明時(shí)使用 public 修飾符來(lái)選擇之,其直觀含義為「不受限制的訪問(wèn)(access not limited)」;
  • Protected,通過(guò)在成員聲明時(shí)使用 protected 修飾符來(lái)選擇之,其直觀含義為「僅限該類及其派生類型內(nèi)可訪問(wèn)(access limited to the containing class or types derived from the containing class)」;
  • Internal,通過(guò)在成員聲明時(shí)使用 internal 修飾符來(lái)選擇之,其直觀含義為「僅限本程序內(nèi)可訪問(wèn)(access limited to this program)」;
  • Protected internal(表示 Protected 或 Internal),通過(guò)在成員聲明時(shí)同時(shí)使用 protectedinternal 修飾符來(lái)選擇之,其直觀含義為「僅限本程序內(nèi)或該類及其派生類性內(nèi)可訪問(wèn)(access limited to this program or types derived from the containing class)」;
  • Private,通過(guò)在成員聲明時(shí)使用 private 修飾符來(lái)選擇之,其直觀含義為「僅限于該類型內(nèi)部訪問(wèn)(access limited to the containing type)」。

聲明成員時(shí)被允許使用的可訪問(wèn)性類型取決于該成員所處之上下文。而且當(dāng)成員在聲明時(shí)不帶任何訪問(wèn)控制修飾符(access modifiers),那么聲明所在的上下文會(huì)為其選擇一個(gè)默認(rèn)的聲明可訪問(wèn)性。

  • 命名空間隱式的為 public 聲明可訪問(wèn)性。命名空間聲明不允許有訪問(wèn)控制修飾符。
  • 編譯單元或命名空間內(nèi)的類型聲明允許使用 publicinternal 聲明可訪問(wèn)性,且默認(rèn)的聲明可訪問(wèn)性為 internal。
  • 類成員可以從這五種聲明可訪問(wèn)性中挑選一個(gè),且默認(rèn)的聲明可訪問(wèn)性為 private。(注意,類中聲明一個(gè)某類型的成員也可以從五種聲明可訪問(wèn)性中挑選一個(gè),盡管這個(gè)類型作為命名空間下的成員在聲明時(shí)只有 publicinternal 可選。)
  • 結(jié)構(gòu)成員擁有 public、internalprivate 聲明可訪問(wèn)性,且默認(rèn)聲明可訪問(wèn)性為 private(這是因?yàn)榻Y(jié)構(gòu)隱式密封 sealed)。結(jié)構(gòu)成員如果是在這個(gè)結(jié)構(gòu)內(nèi)聲明的(也就是說(shuō)不是繼承自其基結(jié)構(gòu)),那么其聲明可訪問(wèn)性不可是 protectedprotected internal。(注意,當(dāng)一個(gè)類型聲明為某結(jié)構(gòu)的成員時(shí),可以從 public、internalprivate 中選擇一個(gè)聲明可訪問(wèn)性,盡管這個(gè)類型在命名空間中的聲明可訪問(wèn)性只能是 publicinternal 的。)
  • 接口成員隱式的為 public 聲明可訪問(wèn)性。接口成員聲明不允許有訪問(wèn)控制修飾符。
  • 枚舉成員隱式的為 public 聲明可訪問(wèn)性。枚舉成員聲明不允許有訪問(wèn)控制修飾符。

可訪問(wèn)域

成員可訪問(wèn)域(accessibility domain)由其所在程序文本之區(qū)段(sections,不一定是連續(xù)的)所組成,于該域之內(nèi)可訪問(wèn)成員。為了定義成員的可訪問(wèn)域,若成員未被聲明于一個(gè)類型內(nèi)則謂之「頂級(jí)(top-level)」,若成員被聲明于另一個(gè)類型內(nèi)則謂之「嵌套(nested)」。此外,程序的程序文本(program text)被定義為其所有源文件中的全部文本,而類型的程序文本也被定義為其類型(及其嵌套類型)聲明中的全部文本。

預(yù)定義類型(predefined type,諸如 object、intdouble)的可訪問(wèn)域是沒(méi)有限制的(unlimited)。

頂級(jí)未綁定類型 T(第四章第 4.3 節(jié))的可訪問(wèn)域被聲明在程序 P 中應(yīng)被如下定義:

  • 如果 T 的聲明可訪問(wèn)性是 public 的,則其可訪問(wèn)域是 P 的整個(gè)程序文本以及所有引用了 P 的程序;
  • 如果 T 的聲明可訪問(wèn)性是 internal 的,則其可訪問(wèn)域是 P 的整個(gè)程序文本。

聲明在程序 P 的類型 T 內(nèi)的嵌套成員 M 的可訪問(wèn)類型的定義,遵照以下規(guī)則(注意,M 自身可能也是一個(gè)類型):

  • 如果 M 的聲明可訪問(wèn)性是 public,那么 M 的可訪問(wèn)域與 T 的可訪問(wèn)域一致;
  • 如果 M 的聲明可訪問(wèn)性是 protected internal,設(shè) D 為程序文本 P 與所有派生自 T 類型的程序文本(在 P 外部聲明的)的并集(union),則 M 的可訪問(wèn)域?yàn)?T 的可訪問(wèn)域與 D 的交集(intersection);
  • 如果 M 的聲明可訪問(wèn)性是 protected,設(shè) D 為 T 的程序文本與所有派生自 T 類型的程序文本的并集(union),則 M 的可訪問(wèn)域?yàn)?T 的可訪問(wèn)域與 D 的交集(intersection);
  • 如果 M 的聲明可訪問(wèn)性是 internal,則 M 的可訪問(wèn)域?yàn)?T 的可訪問(wèn)域與 P 的程序文本的交集(intersection);
  • 如果 M 的聲明可訪問(wèn)性是 private,則 M 的可訪問(wèn)域就是 T 的程序文本。

由這些定義可以得知,嵌套成員的可訪問(wèn)域至少是該成員聲明所在的類型的程序文本。此外還能發(fā)現(xiàn)這么一點(diǎn),成員的可訪問(wèn)域絕不比該成員聲明所在類型的可訪問(wèn)域更廣。

直觀地說(shuō),當(dāng)訪問(wèn)類型或成員 M 時(shí),遵循以下步驟進(jìn)行計(jì)算以確保其可被訪問(wèn)到:

  • 首先,如果 M 聲明于一個(gè)類型(相反于編譯單元或命名空間)內(nèi),則當(dāng)該類型不可被訪問(wèn)將引發(fā)一個(gè)「編譯時(shí)錯(cuò)誤」;
  • 其次,如果 M 是 piblic,則允許其被訪問(wèn);
  • 再次,如果 M 是 protected internal,那么訪問(wèn)發(fā)生在 M 聲明所在的程序內(nèi)或訪問(wèn)發(fā)生在派生自 M 的類型(第三章第 5.3 節(jié))進(jìn)行訪問(wèn)時(shí),允許其被訪問(wèn);
  • 第四,如果 M 是 protected,那么訪問(wèn)發(fā)生在 M 聲明所在的類內(nèi)或訪問(wèn)發(fā)生在派生自 M 的類型(第三章第 5.3 節(jié))進(jìn)行訪問(wèn)時(shí),允許其被訪問(wèn);
  • 第五,如果 M 是 internal,那么訪問(wèn)發(fā)生在 M 聲明所在的程序內(nèi),允許其被訪問(wèn);
  • 第六,如果 M 是 private,那么訪問(wèn)發(fā)生在 M 聲明所在的類型內(nèi),允許其被訪問(wèn);
  • 否則,類型或成員是不可訪問(wèn)的,同時(shí)將引發(fā)一個(gè)「編譯時(shí)錯(cuò)誤」。

在下面這個(gè)例子中,

public class A
{
    public static int X;
    internal static int Y;
    private static int Z;
}
internal class B
{
    public static int X;
    internal static int Y;
    private static int Z;
    public class C
    {
        public static int X;
        internal static int Y;
        private static int Z;
    }
    private class D
    {
        public static int X;
        internal static int Y;
        private static int Z;
    }
}

類和成員擁有如下可訪問(wèn)域:

  • AA.X 的可訪問(wèn)域不受限制;
  • A.YB、B.X、B.YB.C、B.C.X 以及 B.C.Y 的可訪問(wèn)域是程序文本所在的程序內(nèi);
  • A.Z 的可訪問(wèn)域是 A 的程序文本;
  • B.ZB.D 的可訪問(wèn)域是 B 的程序文本,包括 B.CB.D 的程序文本;
  • B.C.Z 的可訪問(wèn)域是 B.C 的程序文本;
  • B.D.XB.D.Y 的可訪問(wèn)域是 B 的程序文本,包括 B.CB.D 的程序文本;
  • B.D.Z 的可訪問(wèn)域是 B.D 的程序文本;

如示例所示,成員的可訪問(wèn)域永不會(huì)大于其所在類型(的可訪問(wèn)域)。比方說(shuō)即便成員 X 的聲明可訪問(wèn)性都是 public,但除了 A.X 外其余成員都受制于其所在類型。

如第三章第四節(jié)所解釋的,所有來(lái)自基類的成員(除了實(shí)例構(gòu)造函數(shù)、析構(gòu)函數(shù)和靜態(tài)構(gòu)析函數(shù))由派生類型所繼承。這甚至包括了基類的私有成員。然而,所包含的私有成員的可訪問(wèn)域只能在其聲明的類型內(nèi)部的程序文本內(nèi)。比方說(shuō):

class A
{
    int x;
    static void F(B b) {
        b.x = 1;        // 正確
    }
}
class B: A
{
    static void F(B b) {
        b.x = 1;        // 錯(cuò)誤,x 不可訪問(wèn)
    }
}

B 類從 A 類繼承了私有(private)成員 x。因?yàn)檫@個(gè)成員是私有的,所以它只能在 A 的類主體內(nèi)部可被訪問(wèn)。因此,可以通過(guò) A.F 方法訪問(wèn) b.x,但不能通過(guò) B.F 訪問(wèn) b.x。

實(shí)例成員的受保護(hù)訪問(wèn)

當(dāng)一個(gè)以 protected 修飾的實(shí)例成員被程序文本之外的類訪問(wèn)時(shí),或者當(dāng)一個(gè)以 protected internal 丟失的實(shí)例成員被程序文本之外的類訪問(wèn)時(shí),訪問(wèn)必須發(fā)生在此類的派生類內(nèi)。此外,這個(gè)訪問(wèn)必須通過(guò)在派生類實(shí)例或構(gòu)造自它的類型的實(shí)例來(lái)訪問(wèn)。這個(gè)限制(restriction)組織了一個(gè)派生類訪問(wèn)另一個(gè)派生類的 protected 成員(即使它們派生自同一個(gè)基類)。

假設(shè) B 為基類(它聲明了一個(gè)受保護(hù)的(protected)實(shí)例成員 M),并設(shè) B 為其派生類。在 D 的類主體(class-body)內(nèi)部,須按照以下形式之一訪問(wèn) M:

  • M 形式的非限定(unqualified)的 type-nameprimary-expression;
  • E.M 形式的 primary-expression,假設(shè)類型 E 為 T 或其派生類,T 為 類型 D 或構(gòu)造自 D 的類型;
  • base.M 形式的 primary-expression

除這些形式的訪問(wèn)外,派生類可以在構(gòu)造初始化器(constructor-initializer,第十章第 11.1 節(jié))內(nèi)訪問(wèn)到受保護(hù)的(protected)基類實(shí)例構(gòu)造函數(shù)

public class A
{
    protected int x;
    static void F(A a, B b) {
        a.x = 1;        // 正確
        b.x = 1;        // 正確
    }
}
public class B: A
{
    static void F(A a, B b) {
        a.x = 1;        // 錯(cuò)誤,必須通過(guò) B 的實(shí)例訪問(wèn)
        b.x = 1;        // 正確
    }
}

上例 A 中,可以通過(guò) A 和 B 訪問(wèn)到 x,因?yàn)檫@兩次訪問(wèn)都發(fā)生在 A 的實(shí)例或其派生類中。然而,在 B 中,不能通過(guò) A 的實(shí)例去訪問(wèn) x,因?yàn)?A 不是 B 的派生類。

class C<T>
{
    protected T x;
}
class D<T>: C<T>
{
    static void F() {
        D<T> dt = new D<T>();
        D<int> di = new D<int>();
        D<string> ds = new D<string>();
        dt.x = default(T);
        di.x = 123;
        ds.x = "test";
    }
}

上例中的三個(gè)對(duì) x 的賦值動(dòng)作都是合法的,因?yàn)樗鼈兌纪ㄟ^(guò)構(gòu)造自泛型類型的類類型的實(shí)例進(jìn)行的。

可訪問(wèn)性約束

在 C# 語(yǔ)言的一些構(gòu)造中要求類型至少與其成員或其它類型具有相同的可訪問(wèn)性(be at least as accessible as)。如果 T 的可訪問(wèn)域(accessibility domain)是 M 的可訪問(wèn)域的超集(superset),那么我們可以說(shuō)類型 T 至少擁有與成員或類型 M 相同的可訪問(wèn)性。換句話說(shuō),如果在任何 M 可被訪問(wèn)的上下文中,T 都可被訪問(wèn),那么 T 至少擁有 M 的可訪問(wèn)性。

有以下這些可訪問(wèn)性約束:

  • 類類型的直接基類必須至少與該類類型自身具有相同的可訪問(wèn)性;
  • 接口顯式繼承的基接口必須至少與該接口類型自身具有相同的可訪問(wèn)性;
  • 委托類型的返回類型與形參類型必須至少與該委托自身具有相同的可訪問(wèn)性;
  • 常量類型必須至少與該常量自身具有相同的可訪問(wèn)性;
  • 字段類型必須至少與該字段自身?yè)碛邢嗤目稍L問(wèn)性;
  • 方法的返回類型與形參類型必須至少與該方法自身具有相同的可訪問(wèn)性;
  • 屬性類型必須至少與該屬性自身具有相同的可訪問(wèn)性;
  • 事件類型必須至少與該事件自身具有相同的可訪問(wèn)性;
  • 索引器的類型和形參類型必須至少與其自身具有相同的可訪問(wèn)性;
  • 操作符的返回類型和形參類型必須至少與其自身具有相同的可訪問(wèn)性;
  • 實(shí)例構(gòu)造函數(shù)的參數(shù)類型必須至少與其自身具有相同的可訪問(wèn)性。

在下例中,

class A {...}
public class B: A {...}

B 類將出現(xiàn)「編譯時(shí)錯(cuò)誤」,因?yàn)?A 類不具備至少與 B 類相同的可訪問(wèn)性。同樣,在下例中,

class A {...}
public class B
{
    A F() {...}
    internal A G() {...}
    public A H() {...}
}

H 方法將出現(xiàn)「編譯時(shí)錯(cuò)誤」,因?yàn)?H 方法所返回的類型 A 不具備至少與該方法相同的可訪問(wèn)性。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)