Отражение Гуру: почему мои объекты MethodInfo не равны? - PullRequest
3 голосов
/ 08 декабря 2011

В основном, некоторая внутренняя проверка, которая происходит в статическом методе System.Linq.Expressions.Expression.Bind(), говорит, что «Метод не является средством доступа к свойству» для моего свойства, которое явно является свойством. Используя Reflector, я минимизировал количество кода, вызывающего проблему, и я не могу понять, почему это произойдет. Мое единственное предположение, что это как-то связано с тем фактом, что свойство не принадлежит самому классу, но я думаю, что это все равно должно работать:

Я попытался сделать небольшой пример с наименьшим количеством кода. Приведенный ниже код должен выполняться полностью.

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));
    }
}

Если бы кто-нибудь мог помочь, я был бы очень признателен! Я очень потерян в этот момент.

Редактировать - если вы замените typeof(SubClass) на typeof(Base), это сработает, так что это что-то связанное с этими отношениями. Я думаю, я мог бы обрезать проблему в корне, сделав метод расширения как typeof(SubClass).GetPropertyFromActualClass("Id"), но я не уверен, как выполнить эту проверку.

1 Ответ

6 голосов
/ 08 декабря 2011

Информация о свойствах и методах Base и SubClass действительно не равны, даже если SubClass не имеет собственной реализации, как можно увидеть здесь:

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

Если вы используете mi.ReflectedType вместо mi.DeclaringType, проверка завершится успешно:

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