Lambda expression not returning expected MemberInfo
I'm running into a problem that I did not expect. An example will probably illustrate my question better than a paragraph:
UPDATED: Skip to 开发者_StackOverflowlast code-block for a more eloquent code example.
public class A
{
public string B { get; set; }
}
public class C : A { }
Here is some code from a method:
var a = typeof(C).GetMember("B")[0];
var b = typeof(A).GetMember("B")[0];
Expression<Func<C, string>> c = x => x.B;
var d = (c.Body as MemberExpression).Member;
Here are the results of some comparisons:
a == b //false
a == d //false
b == d //true
The first two are somewhat unexpected. I understand that even though B is not virtual, C could define a property with the same name with thew new
operator, but in this case I did not.
The second is really the most surprising to me (and is the heart of my problem). Even though the parameter for the lambda is clearly defined as being of type C, it still returns it as if the property was accessed from the base class.
What I'm looking for is a way to get the MemberInfo from a lambda expression as if I had used reflection on the type of the parameter to get the MemberInfo. My project essentially stores MemberInfos in a dictionary of sorts and it needs to have functionality where you can access the elements by providing a lambda expression.
Restated code sample by Danny Chen
public class Base
{
public string Name { get; set; }
}
public class Derived : Base { }
//in Main
var parentMember = typeof(Base).GetMember("Name")[0];
var childMember = typeof(Derived).GetMember("Name")[0];
Expression<Func<Base, string>> parentExp = x => x.Name;
var parentExpMember = (parentExp.Body as MemberExpression).Member;
Expression<Func<Derived, string>> childExp = x => x.Name;
var childExpMember = (childExp.Body as MemberExpression).Member;
parentMember == childMember //false, good
parentMember == parentExpMember //true, good
childMember == childExpMember //false, why?
Take the type of the expression's (first) parameter, and say
Expression<Func<C, string>> c = x => x.B;
Type paramType = c.Parameters[0].Type; // first parameter of expression
var d = paramType.GetMember((c.Body as MemberExpression).Member.Name)[0];
What I'm looking for is a way to get the MemberInfo from a lambda expression as if I had used reflection on the type of the parameter to get the MemberInfo.
That's not a service that expression tree conversions on lambdas were designed to provide. If you're going to use a feature "off label" then you might not get the results that you want.
The purpose of expression trees is to proffer up the compiler's semantic analysis of the expression in a form amenable to analysis at runtime, rather than compile time for the purpose of constructing query objects that can be remoted over to databases.
The compiler's correct semantic analysis is that Name is declared as a property of Base and invoked on an instance of Derived, so that's exactly the information that you get out of the resulting expression tree.
Nice question. I use some other names to make it clearer.
public class Base
{
public string Name { get; set; }
}
public class Derived : Base { }
//in Main
var parentMember = typeof(Base).GetMember("Name")[0];
var childMember = typeof(Derived).GetMember("Name")[0];
Expression<Func<Base, string>> parentExp = x => x.Name;
var parentExpMember = (parentExp.Body as MemberExpression).Member;
Expression<Func<Derived, string>> childExp = x => x.Name;
var childExpMember = (childExp.Body as MemberExpression).Member;
parentMember == childMember //false, good
parentMember == parentExpMember //true, good
childMember == childExpMember //false, why?
When debugging you will find childExpMember.ReflectedType
is Base
, while childMember.ReflectedType
is Derived
. AFAIK DeclaringType
shows where the member is declared, while ReflectedType
shows where the member is reflected to (because of inheritance/overriding/etc). So I think it's a bug (no official confirmation).
I believe you need to pass the flag "FlattenHierarchy" into the bindingAttr parameter of GetMember.
From MSDN:
Specifies that public and protected static members up the hierarchy should be returned. Private static members in inherited classes are not returned. Static members include fields, methods, events, and properties. Nested types are not returned.
精彩评论