作者: leafwiz www.ASPCool.com 时间:2004-11-6 15:50:57 阅读次数:1811
今天朋友问到,为什么在C#中inner class不能够访问外部类的非静态成员,如下面的一段代码就存在这样的问题:
public class TestOuter
{
public static void outer(){}
public void outer2(){}
internal class A
{
public voidtest()
{
outer(); // 可以调用外部静态方法,无法调用实例方法
outer2(); // 本句无法通过编译
}
}
}
在Java中,下面的代码是可以很正常的使用的:
public class testinner
{
static public void Main(String[] args)
{
testinner tester = new testinner();
testinner.Inner inner = tester.new Inner();
inner.testinner();
}
public void test(){ }
class Inner
{
public void testinner()
{
test();
}
}
}
对比之下,C#中的内部类能够使用外部类定义的类型和静态方法,但是不能直接使用外部类的实例方法,直接看来,外部类对于内部类的作用更像是一个命名空间,在C#中,始终可以用(只要访问控制允许)
TestOuter.A instance = new TestOuter.A();
来创建一个内部类的实例,这个实例与外部类的任何实例没有任何直接的关系。类似于Java中的静态内部类。
Java中,非静态内部类可以访问所有外部类的方法和变量。所以Java中的内部类的构造也依赖于外部类,必须使用如:
Outer.Inner inner = outer.new Inner();
这样的语法来定义内部类的实例以确保他与某一确定的外部类的对象相对应。
而在C#中,类区分为Nested Class和Not-Nested Class,前者是声明在其他数据类型内部的类。后者是直接定义在某一个命名空间的类。
非内嵌类只允许使用public和internal的访问控制,而内置类则允许使用所有的五种访问控制符,private, protected , internal protected。内部类也可以访问外部类的所有方法,包括instance方法和private方法,但是需要显式的传递一个外部类的实例。
如:
public class TestOuter
{
public void outer2(){}
internal class A
{
public A(TestOuter obj)
{
Outer_this = obj
}
public void test()
{
obj.outer2();
}
TestOuter Outer_this;
}
}
C#这样做的原因,主要是为了避免outer.new 这样的语法,保持一致的对象创建方式。虽然相比于Java需要创建一个新的方法和变量,但是对象的创建过程更加直接,而且避免了隐含的内部类与外部类的实例之间的关系。
如果分析到CLR的实现层面,我们也可以知道,C#的内部类应该并没有包含外部类的虚函数表,而仅仅包含了静态方法表,所有对外部类的方法的调用时通过外部类指针作的。具体的机制可能更复杂。但是除此以外,内部类并没有被作为一种特殊的类型处理,而是必须与其他普通的对象采取一样的机制进行创建。
C#的内部类提供了覆盖的功能,在一个包含了内部类的类的子类中可以用关键字new 来覆盖同名内部类的实现。
如:
class A1
{
class B1 { }
}
class A2 : A1
{
new class B1 { }
}
C#中的内部类会覆盖同名的外部类的方法。如果上面的A1中定义任何名为B1()的方法,该方法将被覆盖。
内部类使用的一些General Information:
创建内部类的一个目的是为了抽象外部类的某一状态下的行为,或者内部类仅在外部类的某一特定上下文存在。或是隐藏实现,通过将内部类设为private,可以设置仅有外部类可以访问该类。内部类的另外一个重要的用途是当外部类需要作为某个特定的类工作,而外部类已经继承与另外一个类的时候,因为Java不支持多继承,所以创建一个对应的内部类作为外部类的一个façade来使用。
通常,创建内部类的动机都是上面中的其中之一或者几项。而其中最常见的目的莫过于前两项。
我们看一下Java中处理事件的代码:
public class Demo1 extends JPanel
{
class IconDemo implements Icon {}
}
这个例子对应了上面的第一种动机,即封装特定状态或特定实现的动机。IconDemo仅在的Demo1中使用,对应特定的图标绘制行为。
常见的另外一个场合是需要自定义Action的行为,我们可能有如下的类:
public class Demo1 extends JPanel implements ActionListener
{
private JMenuItem item1;
void foo()
{
Item1 = new JMenuItem(“test” , new TestAction(“some action”));
}
Private TestAction extends AbstractAction
{
//code goes here
}
}
这种情况下,TestAction可以看成是一种特化的要求。
隐藏实现的需求往往对应于内部抽象,或者二次抽象(Secondary Abstraction),它对应着对于类内部的部分行为进一步抽象,聚簇。常见的一种情况是在内部类中创建私有的结构,用于抽象尽在类的内部使用的数据结构。
而作为Façade使用的情况相当于外部类作为内部类的创建工厂,当外部请求需要外部类提供一个它不能够继承的基类或接口的时候,外部类产生一个内部类的实例对象,返回给相应的请求。
创建内部类的一些原则:
如果一下一些情况出现,避免使用内部类:
内部类的功能过分膨胀影响了对外部类的阅读
内部类内部包含内部类,对代码的可读性影响很大
内部类可以不依赖于外部类被使用,在这种情况下,说明内部类与外部类的抽象没有包含关系,则应该将内部类作为一种独立的抽象设计为另外一个外部类。(可以参考内部类设计的第二目的,内部抽象)
注:在VC中可以使用内部类作为模版的局部特化处理,掠过不提。其实我也不太懂,:)