Expression for Type members results in different Expressions (MemberExpression, UnaryExpression)
Solution 1
The reason this happens is that Age
is a value type. In order to coerce an expression returning a value type into Func<Person,object>
the compiler needs to insert a Convert(expr, typeof(object))
, a UnaryExpression
.
For string
s and other reference types, however, there is no need to box, so a "straight" member expression is returned.
If you would like to get to the MemberExpression
inside the UnaryExpression
, you can get its operand:
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);
}
Solution 2
Rather than comparing the Member.Name
string, I would suggest comparing the PropertyInfo
instances directly for equality, in order to avoid false positives when two properties in distinct classes share the same name.
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);
}
You can ensure that your lambda expression is compiled as a MemberExpression
rather than a UnaryExpression
simply by specifying the correct value type (rather than object
) as the generic type TResult
of your Expression<Func<T, TResult>>
expression.
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
Related videos on Youtube
dknaack
Updated on September 15, 2022Comments
-
dknaack over 1 year
Description
I have a expression to point on a property of my type. But it does not work for every property type. "Does not mean" means it result in different expression types. I thought it will ever result in a
MemberExpression
but this is not the case.For
int
andGuid
it results in aUnaryExpression
and forstring
in aMemberExpression
.I am a little confused ;)
Some sample code
My class
public class Person { public string Name { get; set; } public int Age { get; set; } }
Test Code
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;
Question
How can i compare two expressions and check if they are mean the same type and same property ?
Update, Answer and complete Sample
Thanks to user dasblinkenlight who brought me on the right track.
He provided the method
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); }
I wrote the following extension method to compare the results of the
GetMemberExpression
methods and check ifGetMemberExpression().Member.Name
are the same.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; }
-
Russell Troywest over 11 yearsAs for a workaround - could you use Expression<Func<Person, TResult>> and pass the type of the property you are returning? That way you wont need to convert it.
-
dknaack over 11 years@dasblinkenlight Thanks a lot! Check out my updated answer. Have a nice day!
-
Scott almost 8 yearsIf you really want to blow minds, you can use C#6's new nullable property accessors to make this a (rather unreadable) one-liner:
MemberExpression member = ((exp.Body as UnaryExpression)?.Operand ?? exp.Body) as MemberExpression