I have a Parent / Child class hierarchy where the Parent abstractly declares a string property and the Child class implements it:
abstract class Parent
{
public abstract string Value { get; }
}
class Child : Parent
{
public override string Value { get { return null; } }
}
When I use an expression that explicitly (or implicitly) uses the Child class, I expect the Expressions's MemberInfo's DeclaringType to be 'Child', but instead it is Parent:
Child child = new Child();
Expression<Func<string>> expression = (() => child.Value);
MemberInfo memberInfo = expression.GetMemberInfo();
Assert.AreEqual(typeof(Child), memberInfo.DeclaringType); // FAILS!
The assertion fails because the DeclaringType is Parent.
Is there something I can do in declaring my expression or consuming it to reveal the actual use of the Child type?
NOTE: GetMemberInfo() above as an extension method (I even forgot we had written this!):
public static class TypeExtensions
{
/// <summary>
/// Gets the member info represented by an expression.
/// </summary>
/// <param name="expression">The member expression.</param>
/// <returns>The member info represeted by the expression.</returns>
public static MemberInfo GetMemberInfo(this Expression expression)
{
var lambda = (LambdaExpression)expression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)lambda.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else memberExpression = (MemberExpression)lambda.Body;
return memberExpression.Member;
}
}
If you don't want the method of the static type you work on, but rather the latest override, then it is possible. I didn't test, but something similar to the following should do the job:
Where you pass the
MemberInfo
as the first param, and the runtime type of the object as second. Note that this is likely slow, so you might want to add some caching.No - this is an accurate representation of what gets emitted by the C# compiler. The override is effectively ignored when looking for the member - the compiler only cares about the type that originally declared the member. You can see this for yourself by compiling code and then looking at the IL. This method:
is compiled into this IL:
One point of trivia: the VB compiler doesn't work the same way, so this method:
is compiled as:
My solution, based on information from @JonSkeet and @CodeInChaos is to not look purely at the PropertyInfo in the Expression, but also the Type of the MemberExpression's Member component: