C 小測試(二) 巢狀子類帶來的困惑

2021-09-05 19:25:05 字數 2017 閱讀 8743

這裡有個很有意思的題目,先別執行程式,猜猜它會輸出什麼?

public

class a

public

class c : b

}
}
class program
} 

這裡的t是int、string還是其它的什麼?還是程式壓根兒就不能通過編譯?

答案是system.int32,你猜對了嗎?很可能這跟你的期望值並不一致。我們來看看它背後的玄妙之處。

有人認為c.m方法輸出int32,因為b的宣告b : a告訴b,t將一直是int。這好像是有道理的,卻不是真正的原因。我們先不管c,執行這一行**(new a.b()).m(),它會輸出:string。b是a的子類並不意味著b中的t總是int

問題的根本在於宣告class c : b會產生不明確的內容:即是class c : a.b 還是class c : a.b?我們得首先搞清楚這個問題。

先看繼承和巢狀類的區別:

public

class x

}
public

class y

public

class z : x

}

(new y.z()).m(); // 合法,m是繼承自基類的方法

(new y.z()).n(); // 不合法,外部類的成員不是內部類的方法

在我們的測試題中,我們呼叫了a.b.c類的方法m,它本質上則呼叫了基類的m方法。如果基類是a.b,那麼應呼叫a.b.m,即輸出t的當前值:string;如果基類是a.b,那麼應呼叫a.b.m,即總是輸出:int32。

而程式的結果告訴我們c#選擇了後者。是不是有些不可思議?

也許程式中的泛型擾亂我們的直覺。那就看乙個不使用泛型的例子:

public

class d

}
public

class f

public

class g

}
public

class h : d

ok,目前為止,一切都是合法的,而且也沒什麼異議。當我們通過型別名稱來引用型別時,型別可以來自於基類(h.e),也可以來自於外部類(g.e)。但是如果這兩種情況同時出現會如何呢?

public

class j

public

class k : d

}

這種情況是合法的嗎?答案是肯定的。此時c#認為基類要優先於外部類。這也合乎常理,派生類與基類的關係是「is-a」,內部類與外部類的關係是「包含於」,前者要比後者更為緊密。

還可以這麼理解:從基類繼承下來的成員都屬於「當前的作用域」,因此從「外部的作用域」獲得的成員的優先順序要低一些。

一般地,對於乙個型別s,在其上下文中對乙個名稱進行解析的演算法是:

現在回到開始的問題。我們看看在解析c的基類的時候發生了什麼。我們呼叫的是b.m(),所以問題可以轉化為找到正確的b。首先c沒有型別引數,也沒有內部類。

a.b的基類是a,而外部類則是a,兩者都有乙個名稱為b的內部類。選哪乙個呢?根據上面的演算法,基類優先,即a.b。

這個過程實在是非常的繞,雖然跟著文章得出了結論,還是有些模糊。。。

參考:

an inheritance puzzle, part one

an inheritance puzzle, part two

C 小測試(二) 巢狀子類帶來的困惑

這裡有個很有意思的題目,先別執行程式,猜猜它會輸出什麼?public class apublic class c b class program 這裡的t是int string還是其它的什麼?還是程式壓根兒就不能通過編譯?答案是system.int32,你猜對了嗎?很可能這跟你的期望值並不一致。我們...

C 測試試卷二

基類中的任何private在派生類中都是不可訪問的。2 若類a和類b沒有繼承關係,對於函式void func a 請至少用兩種不同的方法說明如何才能傳遞乙個非常量的b類物件給func函式。方法一 可在a類中定義乙個建構函式 a const b 方法二 可在b 類中定義乙個自動轉換函式 operato...

小測試C 中反射的效能

using system using system.collections.generic using system.linq using system.web using system.web.ui using system.web.ui.webcontrols using system.refl...