使用反射来调用重写的基本方法

时间:2010-12-05 06:49:24

标签: c# .net reflection

如何使用反射调用被派生类重写的基本方法?

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'...

9 个答案:

答案 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)

答案 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。 希望,这解释了为什么你不能用派生对象

调用基函数