How to use reflection call a base method that 开发者_StackOverflow中文版is overridden by derived class?
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();
}
This code always shows 'Derived'...
After a long time, I finally find a better solution than 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";
}
}
This solution use the standard constructor signature of delegate:
public Delegate(object target, IntPtr ftn)
where target is the target instance and ftn is the function pointer. It directly invoke it with the function pointer of base method, so the delegate will point to the actual base method, not the overridden method.
Even though the current answer is already accepted, it's actually possible without having to change the original class by using a dynamic method like this:
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();
}
as you can see it's still relatively simple to do
This can be achieved through Code emit
http://blogs.msdn.com/b/rmbyers/archive/2008/08/16/invoking-a-virtual-method-non-virtually.aspx
You can't do that, even with reflection. Polymorphism in C# actually guarantees that Derived.Foo()
will always be called, even on an instance of Derived
cast back to its base class.
The only way to call Base.Foo()
from a Derived
instance is to explicitly make it accessible from the Derived
class:
class Derived : Base
{
public override void Foo()
{
Console.WriteLine("Derived");
}
public void BaseFoo()
{
base.Foo();
}
}
Here is a general form of @Kii answer above:
// 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);
}
usage:
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
What you are seeing is the polymorphic behaviour that is by design. When you override a virtual method, invoking that method on the overridden class calls the decendant class's implementation from the VMT.
What is your use case, to be honest this smells a little like a design problem.
Reflection allows you to see that the object d has a "Foo" method and also to invoke it.
This method however is a virtual method and that's why you're getting the implementation of that method by a Derived class since that is what d is (in addition to also being castable to a Base).
There is no [direct] way to invoke the Base's virtual methods from a Derived object.
As shown in in Frederic Hamidi's, the Base class' method can be exposed by the Derived class (under a different name), but that's not really invoking the Base's method, it is invoking a method of the Derived class which happens to call the Base's method.
Although this approach of having the Derived class supply a "proxy" to the method of the Base class, ultimately does what you ask for, it is probably a bad idea to do this: there's likely a flaw in the design of your object model: it would be a rather odd use case...
Base b = (Base)d;
Console.WriteLine(b.GetType()); //output:Derived
1)Casting cannot change it's class type.
class Derived : Base
{
public override void Foo() { Console.WriteLine("Derived"); }
public Base getBase()
{
return base; //compiler invalid
}
}
2) Above are invalid, because you never created any Base
object instance when you created Derived
object instance.
You created instance object of Derived class
which inherited from Base class
.
Hope, that explains why you could not invoke base function with derived object
Maybe Kii was looking for something like this
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();
}
精彩评论