為什么在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中可以使用內部類作為模版的局部特化處理,掠過不提。其實我也不太懂,:)