web-dev-qa-db-ja.com

Typeメンバーの式は異なる式になります(MemberExpression、UnaryExpression)

説明

私のタイプのプロパティを指す式があります。ただし、すべてのプロパティタイプで機能するわけではありません。 「意味しない」とは、さまざまな式のタイプをもたらすことを意味します。 MemberExpressionになると思っていましたが、これは当てはまりません。

intGuidの場合はUnaryExpressionになり、stringの場合はMemberExpressionになります。

私は少し混乱しています;)

いくつかのサンプルコード

私のクラス

_public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
_

テストコード

_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;
_

質問

2つの式を比較して、それらが同じ型で同じプロパティを意味するかどうかを確認するにはどうすればよいですか?

サンプルの更新、回答、完了

ユーザーに感謝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);
}
_

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;
}
_
41
dknaack

これが発生する理由は、Agevalue型であるためです。値の型を返す式を_Func<Person,object>_に強制するために、コンパイラは Convert(expr, typeof(object)) 、a UnaryExpression を挿入する必要があります=。

ただし、stringsおよびその他の参照型の場合、ボックス化する必要がないため、「ストレート」メンバー式が返されます。

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);
}
_
77
dasblinkenlight

Member.Name文字列を比較するのではなく、PropertyInfoインスタンスを直接比較して等しいかどうかを比較し、異なるクラスの2つのプロパティが同じ名前を共有する場合の誤検知を回避することをお勧めします。

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

Expression<Func<T, TResult>>式のジェネリック型MemberExpressionとして正しい値型(UnaryExpressionではなく)を指定するだけで、ラムダ式がobjectではなく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
4
Douglas