如何使用反射调用被派生类重写的基本方法?
class Base
{
public virtual void Foo() { Console.WriteLine("Base"); }
}
class Derived : Base
{
public override void Foo() { Console.WriteLine("Derived"); }
}
public static void Main()
{
Derived d = new Derived();
typeof(Base).GetMethod("Foo").Invoke(d, null);
Console.ReadLine();
}
此代码始终显示'Derived'...
答案 0 :(得分:33)
即使当前的答案已被接受,但实际上可以通过使用这样的动态方法来更改原始类:
static void Main(string[] args)
{
Derived foo = new Derived();
foo.Foo();
MethodInfo method = typeof(Base).GetMethod("Foo");
DynamicMethod dm = new DynamicMethod("BaseFoo", null, new Type[] { typeof(Derived) }, typeof(Derived));
ILGenerator gen = dm.GetILGenerator();
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Call, method);
gen.Emit(OpCodes.Ret);
var BaseFoo = (Action<Derived>)dm.CreateDelegate(typeof(Action<Derived>));
BaseFoo(foo);
Console.ReadKey();
}
你可以看到它仍然相对简单
答案 1 :(得分:32)
经过很长一段时间,我终于找到了比DynamicMethod更好的解决方案:
class CallOverride
{
public static void Test()
{
var obj = new Override();
var method = typeof(object).GetMethod("ToString");
var ftn = method.MethodHandle.GetFunctionPointer();
var func = (Func<string>)Activator.CreateInstance(typeof(Func<string>), obj, ftn);
Console.WriteLine(func());
}
}
class Override
{
public override string ToString()
{
return "Nope";
}
}
此解决方案使用委托的标准构造函数签名:
public Delegate(object target, IntPtr ftn)
其中target是目标实例,ftn是函数指针。 它使用base方法的函数指针直接调用它,因此委托将指向实际的基本方法,而不是重写的方法。
答案 2 :(得分:4)
即使有了反思,你也做不到。 C#中的多态性实际上保证始终会调用Derived.Foo()
,即使在Derived
转换回其基类的实例上也是如此。
从Base.Foo()
实例调用Derived
的唯一方法是明确地从Derived
类访问它:
class Derived : Base
{
public override void Foo()
{
Console.WriteLine("Derived");
}
public void BaseFoo()
{
base.Foo();
}
}
答案 3 :(得分:2)
这可以通过Code emit
来实现http://blogs.msdn.com/b/rmbyers/archive/2008/08/16/invoking-a-virtual-method-non-virtually.aspx
答案 4 :(得分:1)
Reflection允许您看到对象d具有“Foo”方法并且还可以调用它。
然而,这个方法是 虚方法 ,这就是为什么你要通过Derived类获得该方法的实现,因为这就是d(除了也可以施放到基地。)
没有[直接]方法从派生对象调用Base的虚拟方法。
如Frederic Hamidi所示,Base类的方法可以通过Derived类(在不同的名称下)公开,但是它并没有真正调用Base的方法,它正在调用Derived类的方法,该方法恰好调用Base的方法
虽然这种让Derived类为Base类的方法提供“代理”的方法,最终会满足你的要求,但这样做可能是一个坏主意:你的设计可能存在缺陷。对象模型:这将是一个相当奇怪的用例...
答案 5 :(得分:1)
以下是@Kii回答的一般形式:
// returns a delegate (of type methodType) that calls obj.base.method(..)
static object VirtualMethodBase(Type methodType, string methodName, Object obj) {
var method = obj.GetType().BaseType.GetMethod(methodName);
var ftn = method.MethodHandle.GetFunctionPointer();
return Activator.CreateInstance(methodType, obj, ftn);
}
用法:
public class Base {
public override string ToString() => "Base";
public virtual int F(int x, int y) => x+y;
}
public class Derived : Base {
public override string ToString() => "Derived";
public override int F(int x, int y) => x*y;
}
var b = new Base();
var d = new Derived();
var sb = b.ToString(); // "Base"
var fb = b.F(2, 3); // 5
var sd = d.ToString(); // "Derived"
var fd = d.F(2, 3); // 6
// obj.base.ToString()
static string ToStringBase(object obj) => ((Func<string>)VirtualMethodBase(typeof(Func<string>), "ToString", obj))();
// obj.base.F(x, y)
static int FBase(Base bobj, int x, int y) => ((Func<int, int, int>)VirtualMethodBase(typeof(Func<int, int, int>), "F", bobj))(x, y);
var sd1 = ToStringBase(d); // "Base"
var fd1 = FBase(d, 2, 3); // 5
答案 6 :(得分:0)
您所看到的是设计中的多态行为。当重写虚方法时,在重写的类上调用该方法会从VMT调用后代类的实现。
你的用例是什么,说实话,这有点像设计问题。
答案 7 :(得分:-1)
也许Kii正在寻找像这样的东西
class Base
{
public virtual void Foo()
{
Console.WriteLine("Base");
}
}
class Derived : Base
{
// Change virtual with new
// public override void Foo() { Console.WriteLine("Derived"); }
public new void Foo()
{
Console.WriteLine("Derived");
}
}
static void Main(string[] args)
{
Derived d = new Derived();
d.Foo();// Output: Derived
typeof(Base).GetMethod("Foo").Invoke(d, null);// Output: Base
// Or you can cast
((Base)d).Foo();// Output: base
Console.ReadLine();
}
答案 8 :(得分:-2)
Base b = (Base)d;
Console.WriteLine(b.GetType()); //output:Derived
1)施法不能改变它的类类型。
class Derived : Base
{
public override void Foo() { Console.WriteLine("Derived"); }
public Base getBase()
{
return base; //compiler invalid
}
}
2)上面的内容无效,因为在创建Base
对象实例时,您从未创建任何Derived
对象实例。
您创建了Derived class
的实例对象,该对象继承自Base class
。
希望,这解释了为什么你不能用派生对象