Dynamically get value from overriden property

2019-06-11 08:18发布

问题:

I would like to be able to dynamically get the value of an overriden property at runtime using reflection. For example,

class A {
    public virtual int Foo => 5;

    //This implementation doesn't work
    public int ParentFoo => (int)this.GetType().BaseType.GetProperty(nameof(Foo)).GetValue(this); 
}

class B : A {
    public override int Foo => 7;
}

var test = new B();
Console.WriteLine(test.Foo);       //"7"
Console.WriteLine(test.ParentFoo); //Should display "5"

The point of doing this is that the type hierarchy is fairly deep and I don't want extenders to have to re-implement ParentFoo each time with the exact same logic (public int ParentFoo => base.Foo;). I don't mind paying the performance cost for reflection- this property doesn't need to be performant.

Is it possible to accomplish what I need here using reflection?

回答1:

It is possible to always call the original defining class's method for the property using reflection. This is a bad idea. The following code illustrates the concept but isn't battle worthy, and shouldn't be made battle worthy.

void Main()
{
    var a = new A();
    Console.WriteLine(GetNoVCall<A, int>(a, z => z.Foo)); // prints 5
    var b = new B();
    Console.WriteLine(GetNoVCall<A, int>(b, z => z.Foo)); // prints 5
    Console.WriteLine(GetNoVCall<B, int>(b, z => z.Foo)); // prints 5
}

class A
{
    public virtual int Foo { get { return 5; } }
}

class B : A
{
    public override int Foo { get { return 7; } }
}

public static TProp GetNoVCall<TClass, TProp>(TClass c, Expression<Func<TClass, TProp>> f)
{
    var expr = f.Body as MemberExpression;
    var prop = expr.Member as PropertyInfo;
    var meth = prop.GetGetMethod(true);
    var src = expr.Expression as ParameterExpression;

    if (src == null || prop == null || expr == null)
        throw new Exception();

    var dyn = new DynamicMethod("GetNoVCallHelper", typeof(TProp), new Type[]{ typeof(TClass) }, typeof(string).Module, true);
    var il = dyn.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Call, meth);
    il.Emit(OpCodes.Ret);

    return ((Func<TClass, TProp>)dyn.CreateDelegate(typeof(Func<TClass, TProp>)))(c);
}