Дерево выражений с наследованием свойств вызывает исключение аргумента - PullRequest
4 голосов
/ 23 марта 2010

После этого поста: текст ссылки Я пытаюсь создать дерево выражений, которое ссылается на свойство свойства.Мой код выглядит так:

public interface IFoo
{
    void X {get;set;}
}

public interface IBar : IFoo
{
    void Y {get;set;}
}

public interface IFooBarContainer
{
    IBar Bar {get;set;}
}

public class Filterer
{
     //Where T = "IFooBarContainer"
     public IQueryable<T> Filter<T>(IEnumerable<T> collection)
     {
              var argument = Expression.Parameter(typeof (T), "item");

              //...

               //where propertyName = "IBar.X";
               PropertyOfProperty(argument, propertyName); 
     }

        private static MemberExpression PropertyOfProperty(Expression expr, string propertyName)
        {
            return propertyName.Split('.').Aggregate<string, MemberExpression>(null, (current, property) => Expression.Property(current ?? expr, property));
        }
}

Я получаю исключение:

System.ArgumentException: свойство экземпляра 'X' не определено для типа 'IBar'

ReSharper превратил код по ссылке выше в сжатый оператор в моем примере.Обе формы метода вернули одну и ту же ошибку.

Если я ссылаюсь IBar.Y, метод не завершается ошибкой.

1 Ответ

8 голосов
/ 23 марта 2010

Свойство, к которому вы пытаетесь получить доступ, - , а не IBar.X, это IFoo.X. Метод Expression.Property ожидает фактический тип, который объявляет свойство, а не подтип. Если вы не уверены, попробуйте это:

var prop = typeof(IBar).GetProperty("X");

Возвращает ноль (только потому, что IBar является интерфейсом; он будет работать для класса)

Я думаю, что самый простой способ заставить его работать, это создать вспомогательный метод для разрешения фактического свойства, рекурсивно поднимаясь по иерархии типов:

private PropertyInfo GetProperty(Type type, string propertyName)
{
    PropertyInfo prop = type.GetProperty(propertyName);
    if (prop == null)
    {
        var baseTypesAndInterfaces = new List<Type>();
        if (type.BaseType != null) baseTypesAndInterfaces.Add(type.BaseType);
        baseTypesAndInterfaces.AddRange(type.GetInterfaces());
        foreach(Type t in baseTypesAndInterfaces)
        {
            prop = GetProperty(t, propertyName);
            if (prop != null)
                break;
        }
    }
    return prop;
}

Затем вы можете переписать свой PropertyOfProperty следующим образом:

private static MemberExpression PropertyOfProperty(MemberExpression expr, string propertyName)
{
    return propertyName
               .Split('.')
               .Aggregate<string, MemberExpression>(
                   expr,
                   (current, property) =>
                       Expression.Property(
                           current,
                           GetProperty(current.Type, property)));
}
...