具有属性继承的表达式树会导致参数异常

时间:2010-03-22 22:35:27

标签: .net linq expression-trees

关注这篇文章:link text我正在尝试创建一个引用属性属性的表达式树。我的代码如下所示:

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:Instance   属性'X'未定义类型   'IBAR'

ReSharper将上面链接中的代码转换为我示例中的精简语句。这两种形式的方法都返回了相同的错误。

如果我引用IBar.Y,方法不会失败。

1 个答案:

答案 0 :(得分:8)

您尝试访问的媒体资源不是 IBar.X,而是IFoo.XExpression.Property方法需要声明属性的实际类型,而不是子类型。如果您不相信,请尝试:

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

它返回null(仅因为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)));
}