Выражение для членов типа приводит к разным выражениям (MemberExpression, UnaryExpression)
Описание
У меня есть выражение, указывающее на свойство моего типа.
Но он не работает для каждого типа свойства. "Не означает" означает
это приводит к разным типам выражений. Я думал, что это когда-нибудь приведет к
MemberExpression
, но это не так.
Для int
и Guid
это приводит к a 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;
Вопрос
Как я могу сравнить два выражения и проверить, являются ли они средними
того же типа и того же свойства?
Обновить, ответить и выполнить образец
Благодаря пользователю 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;
}
Ответы
Ответ 1
Причина этого в том, что Age
- тип значения. Чтобы принудить выражение, возвращающее тип значения в Func<Person,object>
, компилятору необходимо вставить Convert(expr, typeof(object))
, UnaryExpression
.
Для string
и других ссылочных типов, однако, нет необходимости вставлять в квадрат, поэтому возвращается выражение "прямое" членство.
Если вы хотите попасть в 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);
}
Ответ 2
Вместо сравнения строки 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);
}
Вы можете убедиться, что ваше лямбда-выражение скомпилировано как 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