Type成员的表达式导致不同的表达式(MemberExpression,UnaryExpression)

dkn*_*ack 42 .net c# lambda expression

描述

我有一个表达式指向我的类型的属性.但它并不适用于每种房产类型."不代表"意味着它会导致不同的表达类型.我认为它会导致a MemberExpression但事实并非如此.

对于intGuid它导致一个UnaryExpressionstringMemberExpression.

我有点困惑 ;)

一些示例代码

我的课

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

测试代码

Person p = new Person { Age = 16, Name = "John" };

Expression<Func<Person, object>> expression1 = x => x.Age;
// expression1.Body = UnaryExpression;

Expression<Func<Person, object>> expression2 = x => x.Name;
// expression2.Body = MemberExpression;
Run Code Online (Sandbox Code Playgroud)

我如何比较两个表达式并检查它们是否意味着相同的类型和相同的属性?

更新,回答并完成示例

感谢用户dasblinkenlight带我走上正轨.

他提供了这种方法

private static MemberExpression GetMemberExpression<T>(
    Expression<Func<T,object>> exp
) {
    var member = expr.Body as MemberExpression;
    var unary = expr.Body as UnaryExpression;
    return member ?? (unary != null ? unary.Operand as MemberExpression : null);
}
Run Code Online (Sandbox Code Playgroud)

我编写了以下扩展方法来比较方法的结果GetMemberExpression 并检查是否GetMemberExpression().Member.Name相同.

private static bool IsSameMember<T>(this Expression<Func<T, object>> expr1, Expression<Func<T, object>> expr2)
{
    var result1 = GetMemberExpression(expr1);
    var result2 = GetMemberExpression(expr2);

    if (result1 == null || result2 == null)
       return false;

    return result1.Member.Name == result2.Member.Name;
}
Run Code Online (Sandbox Code Playgroud)

das*_*ght 78

发生这种情况的原因Age类型.为了强制表达式返回值类型Func<Person,object>,编译器需要插入a Convert(expr, typeof(object)),a UnaryExpression.

string但是,对于s和其他引用类型,不需要box,因此返回"直接"成员表达式.

如果你想进入MemberExpression内部UnaryExpression,你可以得到它的操作数:

private static MemberExpression GetMemberExpression<T>(
    Expression<Func<T,object>> exp
) {
    var member = exp.Body as MemberExpression;
    var unary = exp.Body as UnaryExpression;
    return member ?? (unary != null ? unary.Operand as MemberExpression : null);
}
Run Code Online (Sandbox Code Playgroud)

  • 至于解决方法 - 你可以使用Expression <Func <Person,TResult >>并传递你要返回的属性的类型吗?这样你就不需要转换它. (3认同)
  • 如果你真的想要吹嘘思维,你可以使用C#6的新的可空属性访问器来使它成为一个(相当难以理解的)单行:`MemberExpression member =((exp.Body as UnaryExpression)?. Operand ?? exp.Body )作为MemberExpression` (3认同)

Dou*_*las 6

Member.Name我建议不比较字符串,而是PropertyInfo直接比较实例的相等性,以避免在不同类中的两个属性共享相同名称时出现误报。

public static bool IsSameProperty<TSourceA, TSourceB, TPropertyA, TPropertyB>(
    Expression<Func<TSourceA, TPropertyA>> expA,
    Expression<Func<TSourceB, TPropertyB>> expB)
{
    MemberExpression memExpA = expA.Body as MemberExpression;
    MemberExpression memExpB = expB.Body as MemberExpression;

    if (memExpA == null || memExpB == null)
        return false;

    PropertyInfo propA = memExpA.Member as PropertyInfo;
    PropertyInfo propB = memExpB.Member as PropertyInfo;

    if (propA == null || propB == null)
        return false;

    return propA.Equals(propB);
}
Run Code Online (Sandbox Code Playgroud)

您可以确保您的lambda表达式编译为一个MemberExpression,而不是UnaryExpression通过指定正确的值类型(而不是简单object)作为通用型TResult的的Expression<Func<T, TResult>>表达。

Expression<Func<Person, int>> expression1 = x => x.Age;
Expression<Func<Person, int>> expression2 = x => x.Age;
Expression<Func<Person, string>> expression3 = x => x.Name;

Console.WriteLine(IsSameProperty(expression1, expression2));   // True
Console.WriteLine(IsSameProperty(expression1, expression3));   // False
Run Code Online (Sandbox Code Playgroud)