Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda expression not returning expected MemberInfo

Tags:

c#

lambda

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 last 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?
like image 547
Brian Ball Avatar asked Jul 12 '11 01:07

Brian Ball


4 Answers

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]; 
like image 162
agent-j Avatar answered Sep 30 '22 07:09

agent-j


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.

like image 32
Eric Lippert Avatar answered Sep 30 '22 08:09

Eric Lippert


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).

like image 44
Cheng Chen Avatar answered Sep 30 '22 06:09

Cheng Chen


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.

like image 45
Jason.Net Avatar answered Sep 30 '22 08:09

Jason.Net