类的定义是可以嵌套的,即在类的内部还可以定义其他的类。类内声明的类称为内部类(internal class)或者嵌套类(nested class)。在编译单元或命名空间内声明的类称为顶级类或者非嵌套类型(non-nested class)。
比如,下面的List类中定义了一个private类型的内部类Node:
public class List
{
// private内部类
private class Node
{
public object Data;
public Node Next;
public Node(object data, Node next)
{
this.Data = data; // this是Node类的对象
this.Next = next;
}
}
private Node first = null;
private Node last = null;
// public方法
public void AddToFront(object o) {...}
public void AddToBack(object o) {...}
public object RemoveFromFront() {...}
public object RemoveFromBack() {...}
public int Count { get {...} }
}
内部类和包含它的那个类并不具有特殊的关系。在内部类内,this不能用于引用包含它的那个类的实例成员,而只能引用内部类自己的成员。比如,上述代码中的内部类Node中的this只能引用Node的对象,而不能代表List类的对象。
如果类A是类B的内部类,当需要在内部类A的内部访问类B的实例成员时,可以在类B中将代表类B的实例的this作为一个参数传递给内部类A的构造函数,这样就可以实现在类A的内部对类B的访问。比如:
// NestedClass1.cs
// 内部类的示例
using System;
class Wrapper
{
string name = "Wrapper";
public void F()
{
// 构造内部类实例时,传入包含内部类的类的this实例
Nested n = new Nested(this);
n.G();
}
public class Nested
{
Wrapper thisW; // 用于保存外部类的实例
public Nested(Wrapper w)
{
thisW = w;
}
public void G()
{
Console.WriteLine(thisW.name);
}
}
}
class Test
{
static void Main()
{
Wrapper w = new Wrapper();
w.F();
}
}
Wrapper实例创建了一个Nested实例,并将代表它自己的this传递给Nested的构造函数,这样,就可以对Wrapper的实例成员进行后续访问了。
内部类可以访问包含它的那个类可访问的所有成员,包括该类自己的具有private和protected声明可访问性的成员。比如:
// NestedClass2.cs
// 内部类的示例
using System;
class Wrapper
{
protected string name = "Wrapper";
private void F()
{
Console.WriteLine("Wrapper.F()");
}
public class Nested
{
public void G()
{
Wrapper w = new Wrapper();
Console.WriteLine(w.name);
w.F();
}
}
}
class Test
{
static void Main()
{
Wrapper.Nested n = new Wrapper.Nested();
n.G();
}
}
上述代码中,类Wrapper包含内部类Nested。在Nested内,方法G引用在Wrapper中定义的protected字段name和private方法F()。
内部类的完全限定名为S.N,其中S是声明了N类的那个类的完全限定名。
非嵌套类可以具有public或internal访问修饰符,默认的访问修饰符是internal。但是,内部类具有5种访问修饰符(public、protected internal、protected、internal或private)中的任何一种,而且与其他类成员一样,默认的已访问修饰符是private。
内部类的可访问域受包含它的类的访问修饰符和它自身的访问修饰符的限制。内部类的可访问域至少为包含它的类体。内部类可访问域是声明它的类的可访问域的子集。
内部类的成员的可访问域受包含内部类的类访问修饰符、内部类的访问修饰符和它自身的访问修饰符的限制。内部类成员的可访问域是内部类的可访问域的子集。比如,
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;
}
}
上述代码中的类和成员的可访问域分别为:
— B、B.X、B.Y、B.C、B.C.X和B.C.Y的可访问域是定义类B的程序。
— B.Z和B.D的可访问域是B的代码体,包括B.C和B.D的代码体。
— B.C.Z的可访问域是B.C的代码体。
— B.D.X和B.D.Y的可访问域是B的代码体,包括B.C和B.D的代码体。
— B.D.Z的可访问域是B.D的代码体。
当内部类的成员与定义它的类的成员重名时,内部类成员会隐藏外部类的成员。比如,在上面的例子中,在B.C内直接使用X、Y、Z指的是B.C.X、B.C.Y、B.C.Z,而不是B.X、B.Y、B.Z。