C# 如何(非虚拟地)调用虚拟方法的原始实现?
我有以下情况: 在第三方库中(不能修改): 在我自己的代码中:C# 如何(非虚拟地)调用虚拟方法的原始实现?,c#,.net,C#,.net,我有以下情况: 在第三方库中(不能修改): 在我自己的代码中: class C : B { public override void M() {} } 从C方法M的实现中,我想调用A(而不是B)。我可以吗 接受任何技巧,包括反射。我已经尝试过反射,但是使用我从typeof(A)中获得的MethodInfo仍然会生成一个虚拟调用(调用C的实现,随后堆栈溢出) 从A派生C是不可能的,因为重新实现B的复杂性,我恐怕这不可能像您描述的那样直接实现-虚拟方法的目的是使覆盖透明。因此,要做到这一点,唯一的
class C : B { public override void M() {} }
从C
方法M
的实现中,我想调用A
(而不是B
)。我可以吗
接受任何技巧,包括反射。我已经尝试过反射,但是使用我从typeof(A)
中获得的MethodInfo
仍然会生成一个虚拟调用(调用C
的实现,随后堆栈溢出)
从
A
派生C
是不可能的,因为重新实现B
的复杂性,我恐怕这不可能像您描述的那样直接实现-虚拟方法的目的是使覆盖透明。因此,要做到这一点,唯一的办法就是通过变通办法
让我试着提出一个建议,但请注意,这是一个令人讨厌的建议。如果您的代码中确实需要这个构造,那么它可能表明您的代码在其他地方有一个基本的设计缺陷,因此重构某些东西可能比用另一个设计缺陷填充它更可取。但不管怎样,这是
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(); }
}
在我之前的回答中,我忽略了A和B位于外部库中且无法修改的事实。在这种情况下,我建议采用不同的方法。基本上,如果设计缺陷在B中,您不能使用A中的B.子类
当然,这样做的不幸后果是,您可能需要重新实现B中的部分或全部功能。如果需要,您可以从Reflector复制代码。我意识到这听起来不太理想,但我仍然认为使用不可修改的代码比使用已知问题导致您出现问题的代码更好。您不能这样做。您可能应该以不同的方式设计类层次结构,因为C继承自B,而其行为类似于A,这看起来很奇怪 无论如何,这对你来说是有意义的。然后,您应该在中创建另一个不会重写的方法:
class A {
protected virtual void basicM() {}
public virtual void M() { basicM(); }
}
class C {
public override void M() { basicM(); }
}
顺便说一句,如果您像我在示例中那样命名方法,那么您可能应该重新考虑整个问题。如果这个层次结构是合理的,那么than
basicM
可能会执行一些应该是具有不同名称的单独方法,甚至可能是公共方法的操作。您可以生成动态方法,使代理使用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);
var x=newc();
var m=类型(A).GetMethod(“m”);
var dm=新的动态方法(“代理”,typeof(void),new[]{typeof(C)},typeof(C));
var il=dm.GetILGenerator();
il.Emit(操作码.Ldarg_0);
il.Emit(操作码调用,m);
发射(操作码Ret);
var action=(action)dm.CreateDelegate(typeof(action));
行动(x);
谢谢@Timwi。正如我在问题中所说,不幸的是,A和B是第三方库的一部分,因此不允许在那里进行更改。是的,有一个设计缺陷:在B类中!:-)这是我几乎从不使用继承的原因之一。@ChaosPandion:是的!完全地想想看,为什么一开始还要写代码呢?@Timwi-我知道你只是在开玩笑,但是有更好的方法,比如写作。@ChaosPandion:我知道你是认真的,但是有更好的爱好,比如运动。-我的意思是:根据您的需求使用正确的工具。如果需要虚拟方法,应该使用继承。如果你需要体育锻炼,你应该做运动。@Timwi-我说过“几乎从来没有”对吧?:)阅读问题:'(A
和B
不能被触摸。我不是问“我如何重新设计这个?”我是问如何给A.m
打电话。如果你想给我负面评价,请提供一个评论。我和其他人都会因为知道什么是错的而受益。@Mau阅读答案的第一句话!这个答案非常一致cal给我之前的一个,所以我很惊讶你觉得这一个应该被否决,但我的不应该。这会很好,但在B
中有大量非琐碎的代码。我曾想过IL非病毒调用,但没想到会这么简单。让我试试:-)这很聪明,但如果我看到它被用于生产代码中,以绕过架构设计的限制,它肯定会引起我的注意。
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);