2010-07-31 106 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