Reflection Gurus: Why aren't my MethodInfo obj

2019-07-01 13:40发布

问题:

Basically, some internal check that happens in the static System.Linq.Expressions.Expression.Bind() method says "Method is not a property accessor" on my property that is clearly a property. Using Reflector, I've minimized the amount of code causing the problem, and I can't for the life of me figure out why this would happen. My only guess is that it has something to do with the fact that the property isn't on the class itself, but I would think this should still work:

I've tried to make a small example with the least amount of code possible. The code below should run in its entirety.

using System;
using System.Reflection;

public class Base
{
    public virtual int Id { get; set; }
}

// As you can see, SubClass does not override the Id property of Base.
public class SubClass : Base { }

class Program
{
    static void Main(string[] args)
    {
        // Getting the property directly from the type.
        PropertyInfo propertyInfo = typeof(SubClass).GetProperty("Id");
        MethodInfo setMethod = propertyInfo.GetSetMethod();

        /*  Code from here on out is from the System.Linq.Expressions.Bind() method (the one
            that accepts a MethodInfo argument). This method causes GetProperty to be called
            and retrieve what should be the same PropertyInfo. It fails here, saying something
            along the lines of "Method is not a property accessor." which doesn't make sense. */
        PropertyInfo propertyInfo2 = GetProperty(setMethod);
    }

    private static PropertyInfo GetProperty(MethodInfo mi)
    {
        // Not sure if it matters, but declaringType here is "Base".
        Type declaringType = mi.DeclaringType;

        BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public;
        bindingAttr |= mi.IsStatic ? BindingFlags.Static : BindingFlags.Instance;

        foreach (PropertyInfo info in declaringType.GetProperties(bindingAttr))
        {
            // For the "Id" property, info.CanRead is true, but CheckMethod is false.
            if (info.CanRead && CheckMethod(mi, info.GetGetMethod(true)))
                return info;

            // For the "Id" property, info.CanWrite is true, but CheckMethod is false.
            if (info.CanWrite && CheckMethod(mi, info.GetSetMethod(true)))
                return info;
        }

        // This gets thrown after passing by the "Id" property that is the one I'm looking for.
        throw new Exception("Method is not a property accessor");
    }

    private static bool CheckMethod(MethodInfo method, MethodInfo propertyMethod)
    {
        // These are not equal, so it goes to the next check. In the debugger, they appear identical when I look through the object tree.
        if (method == propertyMethod)
            return true;

        Type declaringType = method.DeclaringType;
        return ((declaringType.IsInterface && (method.Name == propertyMethod.Name)) && (declaringType.GetMethod(method.Name) == propertyMethod));
    }
}

If anyone could help I would greatly appreciate it! I'm very much lost at this point.

Edit - If you substitute typeof(SubClass) with typeof(Base), it works, so it's something related to that relationship. I guess I could cut the issue off at the root by making an extension method like typeof(SubClass).GetPropertyFromActualClass("Id") but I'm not sure how to perform that check.

回答1:

The property and method infos of Base and SubClass are indeed not equal, even if SubClass has no own implementation, as can be seen here:

var subPropertyInfo = typeof(SubClass).GetProperty("Id");
var subPropertyInfoSetter = subPropertyInfo.GetSetMethod();

var basePropertyInfo = typeof(Base).GetProperty("Id");
var basePropertyInfoSetter = basePropertyInfo.GetSetMethod();

Console.WriteLine(subPropertyInfo == basePropertyInfo);     // false
Console.WriteLine(subPropertyInfoSetter == basePropertyInfoSetter); // false

If you use mi.ReflectedType instead of mi.DeclaringType, the check succeeds:

var type = subPropertyInfoSetter.ReflectedType;
Console.WriteLine(type.GetProperty("Id").GetSetMethod() 
                               == subPropertyInfoSetter); // true