开发工具与关键技术:Microsoft Visual Studio 2015、.NET
撰写时间:2019年08月02日
成员的声明允许通过成员访问来控制。成员的可访问性是由一些成员的已经声明的访问性建立的,如果有立即包含(containing)类型,这些成员就同这些立即包含类型相结合。
当访问一个特殊成员被允许时,成员被称为可访问的。相反,当对一个成员的访问被禁止,这个成员就被称为不可访问的。当成员的可访问中包括了访问发生的文本地址时,就允许对一个成员进行访问。
(一)可访问性域
一个成员的可访问性域是(也许是脱节的 )程序文字的一部分,在这里,允许对成员进行访问。为了定义一个成员的可访问性域,如果不在类型里声明,一个成员就被说成是顶级的,而如果它在另外一个类型里声明,这个成员就被称为嵌套的。而且,程序的程序文字就像所有包括在程序源文件中的程序文字一样定义,而一个类型的程序文字就像所有在类、结构接口或枚举的结构体中包含在开始和结束符号 “{” 和 “}”中的程序文字一样定义(可能包括有嵌套的类型)。
一个预定义类型(例如 object、int或 double)的可访问性域是没有限制的。 一个在程序 P中声明的顶级类型 T的可访问性域定义如下:
• 如果 T的声明可访问性是公共的,那么 T的可访问性域是 P的程序文字和任何引用 P的程序。
• 如果 T的声明可访问性是内部的,T的声明可访问性就是 P的程序文字。 从这些它所跟随的定义来看,顶级类型的可访问性域通常至少是声明类型的程序的程序文字。 程序 P中在类型 T中声明的成员 M的可访问性域的定义如下(注意 M本身也许就是个类型):
• 如果 M的声明可访问性是公共的,M的可访问性域是 T的可访问性域。
• 如果 M的声明可访问性是内部保护的,M的可访问性域就是T的可访问性域和P的程序文字的交集和在P外面声明的并且从T继承的程序文字。
• 如果M的声明可访问域是保护的,M的可访问性域就是T的可访问性域和T的程序文字的交集和任 何从T中继承的类型。
• 如果M的声明是内部的,M的可访问性域就是T的可访问性域和P的程序文字的交集。
• 如果M的声明是私有的,M的可访问性域就是T的程序文字。
从这些它所跟随的定义来看,嵌套成员的可访问性域通常至少是声明成员的类型的程序文字。而且,一个成员的可访问性域永远不会比成员被声明的类型的可访问性域包含更多。
从直觉来说,当一个类型或成员M被访问,下面的步骤就是进行估计以确保访问被允许:
• 首先,如果M按一个类型声明(与一个编译单元或名称空间相对),如果那个类型是不可访问的,就会发生错误。
• 这样,如果M是公共的,访问就被允许。
• 另外,如果M是内部保护的,如果访问发生在M被声明的程序中访问就是被允许的,或者如果访问发生在从M被声明的类继承的类中并且是通过派生类类型发生,访问也是被允许的。
• 另外,如果M是保护的,如果访问发生在M被声明的类中,或者在从M被声明的类继承的类中并且是通过派生类类型发生,访问就是被允许的。
• 另外,如果M是内部的,如果发生在M被声明的程序中,访问就是被允许的。
• 另外,如果M是私有的,如果访问发生在M被声明的类型中,访问就是被允许的。
• 另外,如果成员的类型是不可访问的,就会发生错误。
在例子中
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;
}
}
类和成员有下面的可访问性域:
• A和 A.X的可访问性域是没有限制的。
• A.Y、 B、 B.X、 B.Y、 B.C、 B.C.X 和 B.C.Y 的可访问性域是包含程序的程序文字。
• A.Z 的可访问性域是A的程序文字。
• B.Z 和 B.D 的可访问性域是B的程序文字,包括 B.C 和 B.D 的程序文字。
• B.C.Z 的可访问性域是 B.C 的程序文字。
• B.D.X、 B.D.Y 和 B.D.Z 的可访问性域是 B.D 的程序文字。
如所示的例子,一个成员的可访问性域永远不会比比包含类型的大。例如甚至所有X成员有公共的声明可访问性,除了A.X外都有被包含类型约束的可访问性域。
基类中除了构造函数和析构函数的所有成员,都是从派生类型继承的。这甚至包括基类的私有成员。然而,一个私有成员的可访问性域只包括声明成员的类型的程序文字。
在例子中
class A
{
int x;
static void F(B b)
{
b.x = 1; // Ok
}
}
class B : A
{
static void F(B b)
{
b.x = 1; // Error, x not accessible
}
}
类B从类A中继承私有成员X。因为成员是私有的 ,所以只有在A的类结构体中才能对它进行访问。这样在方法 A.F中允许对b.x的访问,但是在方法B.F中是失败的。
(二)保护的访问
当一个保护成员在他被声明的类的程序文字外被访问,并且当一个内部保护成员在他被声明的程序的程序文字外被访问,访问就要求通过访问发生的派生类中进行。让声明了一个保护成员M的B作为一个基类,并且让D作为从B派生的类。在D的类结构体内,可以通过下面的某种形式来访问M:
• 格式 M的一个无效的类型名称和一个基本的表达式。
• 一个格式T.M的基本表达式,T是由D或者从D派生的类提供的。
• 一个格式E.M的基本表达式,E是由D或者从D派生的类提供的。
• 一个格式base.M的基本表达式。
除了这些形式的访问,一个派生类可以在构造函数初始化时访问基类的保护的构造函数。在这个例子中
public class A
{
protected int x; static void F(A a, B b)
{
a.x = 1; // Ok
b.x = 1; // Ok
}
}
public class B : A
{
static void F(A a, B b)
{
a.x = 1; // Error, must access through instance of B
b.x = 1; // Ok
}
}
由于访问或者通过A的实例发生或者在A的派生类中发生,因此,在A中可以通过A和B的实例来访问X。然而,由于A不是从B中派生的,因此在B中不可能通过A的实例访问x。
(三)可访问性约束
C#语言中的许多结构需要一种至少和一个成员或其它类型相同可访问的类型。如果T是M可访问性域的一个超集,那么类型T就要求至少可和成员或类型M一样可访问。换句话说,如果T在所有M可访问的上下文中都可访问,那么T至少和 M一样可访问。
下面的有一些可访问性的约束:
• 一个类类型的直接基类必须至少同类类型本身同样可访问。
• 一个接口类型的外部基本接口必须至少同接口类型本身同样可访问。
• 代表类型的返回类型和参数类型必须至少同代表类型本身同样可访问。
• 常数的类型必须至少同常数本身同样可访问。
• 域的类型必须至少同域本身同样可访问。
• 一个方法的返回类型和参数类型必须至少同方法本身同样可访问。
• 属性的类型必须至少同属性本身同样可访问。
• 事件的类型必须至少同事件本身同样可访问。
• 参数的类型必须至少同索引本身同样可访问。
• 一个操作符的返回类型和参数类型必须至少同操作符本身同样可访问。
• 构造函数的参数类型必须至少同构造函数本身同样可访问
在这个例子中
class A {...}
public class B : A {...}
因为A不能与B一样可访问,所以类比是有错误的。
同样,在例子中
class A {...}
public class B
{
A F(){...}
internal A G(){...}
public A H(){...}
}
因为A不能与B一样可访问,所以B中的方法 H也是有错误的。