新聞中心
注意:如果你是一個初學(xué)者,對實(shí)例方法,虛方法的調(diào)用還不太清楚,強(qiáng)烈建議你不要閱讀本文,因?yàn)檫@里面的代碼會讓你完全崩潰掉。

創(chuàng)新互聯(lián)公司是專業(yè)的建甌網(wǎng)站建設(shè)公司,建甌接單;提供做網(wǎng)站、網(wǎng)站設(shè)計(jì),網(wǎng)頁設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行建甌網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!
如果你對實(shí)例方法,虛方法的運(yùn)行機(jī)制已經(jīng)了如指掌,并且,對方法和對象的內(nèi)存布局也心中有數(shù),那么本文可能會顛覆你以前對他們的認(rèn)識。
閱讀本文的***方式就是親自演練一下,如果看完之后有疑惑,那么是正常的,但是稍加思考就會想明白。
我說,string變量可以直接引用一個object對象!
我說,派生類型的變量可以直接引用基類型的對象!
你會說,老兄,別開玩笑了,派生類型怎么可以指向一個基類型的對象呢!
我會讓你見證一下奇跡,并在文章的結(jié)尾再給你一個更加不可思議的例子。
首先,請看下面的代碼:
- class Program {
- static void Main(string[] args) {
- Derived d=(Derived)new Base();
- d.Print();
- Console.Read();
- }
- }
- class Base {
- public void Print() {
- Console.Write("in base");
- }
- }
- class Derived : Base {
- public new void Print() {
- Console.WriteLine("in derived");
- }
- }
毫無疑問,在運(yùn)行時一定會拋出一個異常,因?yàn)锽ase對象無法轉(zhuǎn)換為Derived對象。
但是,現(xiàn)在,我就想讓d指向Base對象,并且可以調(diào)用Base中的Print方法,該怎么做呢?
用FiledOffset可以做到這一點(diǎn),但首先需要定義一個叫做Manager的類,里面包含兩個實(shí)例字段,一個為Derived,一個為Base。如下:
- [StructLayout(LayoutKind.Explicit)]
- class Manager {
- [FieldOffset(0)]
- public Base b = new Base();
- [FieldOffset(0)]
- public Derived derived;
- }
現(xiàn)在,通過為b和derived都指定了相同的偏移,所以,b和derived都指向了同一個對象,Base對象。
由于derived現(xiàn)在指向了Base對象,那么如果我調(diào)用d.Print方法,調(diào)用的是Base的Printf還是Derived的Print方法,還是拋出一個異常。請看如下代碼:
- class Program {
- static void Main(string[] args) {
- Manager m = new Manager();
- m.derived.Print();
- Console.Read();
- }
- }
運(yùn)行上面代碼,會輸出什么呢?
答案是,“In Derived”。
這很不可思議,因?yàn)閐erived指向的是Base對象,現(xiàn)在調(diào)用的確實(shí)Derived的方法。想要了解原因,請看下圖:
這里,盡管derived指向的是一個Base對象,但是,CLR發(fā)現(xiàn)Print是一個非虛方法,所以CLR并不關(guān)心derived變量指向什么對象,CLR根據(jù)derived變量的類型來調(diào)用Print方法,這里derived是一個 Derived類型,所以CLR會調(diào)用Derived中的Print,最終輸出In Derived。
第二個例子:
下面的這個例子也很不可思議,同樣會顛覆你傳統(tǒng)的觀點(diǎn)。
讓我們將上面的print方法改為virtual方法,最終如下:
- [StructLayout(LayoutKind.Explicit)]
- class Manager {
- [FieldOffset(0)]
- public Base b = new Base();
- [FieldOffset(0)]
- public Derived derived;
- }
- class Base {
- public virtual void Print() {
- Console.Write("in base");
- }
- }
- class Derived : Base {
- public override void Print() {
- Console.WriteLine("in derived");
- }
- }
現(xiàn)在,運(yùn)行如下測試代碼:
- class Program {
- static void Main(string[] args) {
- Manager m = new Manager();
- m.derived.Print();
- Console.Read();
- }
- }
這次結(jié)果會是什么呢?強(qiáng)烈建議你自己思考答案。
結(jié)果是,In Base!
是不是及其不可思議!為了更清楚的理解原因,請看下圖:
這里,盡管derived指向的是Base對象,但是,當(dāng)CLR看到derived.Print這行代碼時,由于Print是虛方法,所以CLR會查看derived所指向的Base對象。
CLR發(fā)現(xiàn)Base對象里的type object pointer指向一個Base type object,于是就調(diào)用Base Type object中的Print方法,所以最終會輸出InBase。
總結(jié):
沒有總結(jié)可不好。
本質(zhì)上,子類型是不能引用父類型對象的。但是,我們可以通過FieldOffset繞過這一限制。通過子類型的變量來調(diào)用父對象的方法,這很是不可思議,但更不思議的是,當(dāng)子類型的變量指向父對象時,竟然可以調(diào)用子方法!
那么上面的本質(zhì)是什么呢?當(dāng)CLR調(diào)用一個非虛方法時,不會關(guān)心變量具體指向的是什么,因?yàn)镃LR此時是通過變量的類型來調(diào)用方法。如果方法時虛方法,那么CLR為了實(shí)現(xiàn)多態(tài),需要查看這個變量指向的是什么對象,然后在通過對象的type object pointer找到對應(yīng)的Type Object,然后調(diào)用Type Object中的方法。
分享文章:顛覆你對方法調(diào)用的看法!
網(wǎng)頁路徑:http://www.5511xx.com/article/dpdeehp.html


咨詢
建站咨詢
