如何调用(非虚拟)虚拟方法的原始实现?

时间:2010-07-31 12:05:27

标签: c# .net

我有以下情况:

在第三方库中(无法修改):

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

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

在我自己的代码中:

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

C的方法M的实现,我想调用A(但不是B的!!)。我可以吗?

接受任何技巧,包括反思。我已经尝试过反射,但是使用MethodInfo得到的typeof(A)仍会生成虚拟调用(调用C的实现,后续堆栈溢出)。

由于重新实现C的复杂性,

从[{1}}导出A是不可能的。

4 个答案:

答案 0 :(得分:16)

您可以生成动态方法来制作使用Call(而不是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);

答案 1 :(得分:1)

我担心这不可能直接以你描述的方式实现 - 虚方法的目的是让覆盖变得透明。因此,实现此目的的唯一方法是通过解决方法。

让我尝试鞭打一个,但请注意这是一个hacky建议。如果你真的需要在你的代码中使用这个结构,那么可能表明你的代码在其他地方有一个基本的设计缺陷,因此重组某些东西可能比填充另一个设计缺陷更令人满意。但无论如何,这里...... ...

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(); }
}

答案 2 :(得分:1)

在我之前的回答中,我错过了A和B在外部库中并且无法修改的事实。在这种情况下,我建议采用不同的方法。基本上,如果设计缺陷在B中,则不能使用A中的B. Subclass。

当然,这种不幸的后果是您可能需要重新实现B中的部分或全部功能。如有必要,您可以从Reflector复制代码。我意识到这听起来不合适,但我仍然认为使用具有已知问题的不可修改的代码会更有可能导致您出现问题。

答案 3 :(得分:0)

你做不到。您应该可能以不同的方式设计您的类层次结构,因为C从B继承而看起来很奇怪,而行为类似于A.

无论如何,它可能在你的情况下有意义。然后你应该在A中制作另一个你不会覆盖的方法:

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

BTW,如果你像我在示例中所做的那样命名方法,那么你应该重新考虑整个事情。如果这个层次结构是合理的,那么basicM可能执行的事情应该是一个具有不同名称的单独方法,甚至可能是一个公共方法。