2010-07-31 199 views
7

我有以下情況:如何調用(非虛擬)虛擬方法的原始實現?

在第三方庫(不能修改):

class A { public virtual void M() {} } 

class B : A { public override void M() {} } 

在我自己的代碼:

class C : B { public override void M() {} } 

C的實現方法M我想打電話給A(但不是B的!!)。我可以嗎?

接受任何招數,包括反思。我已經嘗試了反思,但是使用typeof(A)得到的MethodInfo仍然會生成一個虛擬調用(在隨後的堆棧溢出時調用C的實現)。

A導出C由於重新實現B的複雜性而不存在問題。

+0

這是我幾乎從來不使用繼承的原因之一代理。 – ChaosPandion 2010-07-31 12:23:00

+0

@ChaosPandion:是的!完全!想想看,爲什麼甚至在第一時間編寫任何代碼呢? – Timwi 2010-07-31 12:36:54

+0

@Timwi - 我知道你只是開玩笑,但有更好的方法,如構圖。 – ChaosPandion 2010-07-31 12:45:23

回答

16

您可以生成動態的方法來製造使用呼叫(而不是CallVirt)指令

 var x = new C(); 
     var m = typeof (A).GetMethod("M"); 
     var dm = new DynamicMethod("proxy", typeof (void), new [] {typeof(C)}, typeof (C)); 
     var il = dm.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Call, m); 
     il.Emit(OpCodes.Ret); 
     var action = (Action<C>)dm.CreateDelegate(typeof (Action<C>)); 
     action(x); 
+0

我曾想過IL非虛電話,但並不認爲它會這麼簡單。讓我試試:-) – Mau 2010-07-31 12:43:44

+0

輝煌。它像一個魅力。乾杯! – Mau 2010-07-31 14:11:55

+6

這很聰明,但如果我看到這個被用在生產代碼中來解決建築設計的限制,它肯定會引起我的眉毛。 – 2010-07-31 14:36:52

1

恐怕這是不可能的,直接按照你描述的方式 - 虛擬方法的目的是讓覆蓋變得透明。所以唯一的辦法就是通過解決方法。

讓我試着鞭打一個,但請注意,這是一個黑客的建議。如果你確實需要在你的代碼中使用這個構造,這可能表明你的代碼在其他地方有一個基本的設計缺陷,所以重構某些東西比填充另一個設計缺陷更可取。但無論如何,在這裏去...

class A { 
    public virtual void M() { m_protected(); } 
    protected void m_protected() { /* code goes here */ } 
} 

class B { 
    public override void M() { /* code here, possibly invoking base.M() */ } 
} 

class C { 
    public override void M() { m_protected(); } 
} 
+0

謝謝@Timwi。正如我在問題中所說的,不幸的是,A和B是第三方庫的一部分,因此不允許在那裏進行更改。是的,有一個設計缺陷:在B級! :-) – Mau 2010-07-31 12:14:53

1

在我以前的答案,我錯過了一個事實,即A和B是在外部庫,不能修改。在這種情況下,我會建議採用不同的方法。基本上,如果設計缺陷在B中,則不能使用A中的B. Subclass。

當然,不幸的後果是您可能需要重新實現B中的部分或全部功能。如有必要,您可以從Reflector複製代碼。我意識到這聽起來不合需要,但我仍然認爲最好使用不可修改的代碼來解決導致問題的已知問題。

+0

這會很好,但'B'中有一個TON不重要的代碼。 – Mau 2010-07-31 12:27:53

0

你不能那樣做。你應該可能不同地設計你的類層次結構,因爲它看起來很奇怪,C繼承自B,而行爲像A.

無論如何,它可以在你的情況下有意義。那麼你應該做的另一種方法在一個你不會覆蓋:

class A { 
    protected virtual void basicM() {} 
    public virtual void M() { basicM(); } 
} 
class C { 
    public override void M() { basicM(); } 
} 

順便說一句,如果您命名的方法,我的例子一樣,那麼你或許應該重新考慮整個事情。如果這個層次結構是合理的,那麼basicM可能會執行一些值得用不同名稱的獨立方法,甚至可能是公共方法。

+0

閱讀問題:'('A'和'B'不能被觸摸,我不是在問'我怎樣才能重新設計這個?'我要問怎麼調用'AM' – Mau 2010-07-31 12:27:14

+0

如果你打算 – zvone 2010-07-31 12:28:28

+0

@Mau閱讀答案的第一句話! – zvone 2010-07-31 12:29:18