Lambda表达式未返回预期的MemberInfo

24

我遇到了一个我没有预料到的问题。 一个示例可能比一段话更能说明我的问题:

更新:跳转到最后的代码块以获取更优雅的代码示例。

public class A
{
  public string B { get; set; }
}

public class C : A { }

下面是一个方法的代码:

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;

以下是一些比较的结果:

a == b //false
a == d //false
b == d //true

前两个结果有些出乎意料。我理解即使B不是虚拟的,C也可以使用new运算符定义具有相同名称的属性,但在这种情况下我没有这样做。

第二个结果对我来说真的是最令人惊讶的(也是我的问题所在)。尽管lambda的参数明确定义为C类型,但它仍然返回它,就好像从基类访问了该属性一样。

我正在寻找一种方法,通过lambda表达式获得MemberInfo,就像我在类型上使用反射获取MemberInfo一样。我的项目基本上将MemberInfos存储在某种字典中,并且需要具有通过提供lambda表达式访问元素的功能。

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?
4个回答

22

获取表达式的第一个参数类型,然后说

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];

1
这很可能是我最终要做的事情,但我希望有比实际使用反射更清晰的解决方案。如果一段时间后没有更好的答案,我会将其标记为正确并使用它。 - Brian Ball
2
我查看了几个其他具有类似功能的开源项目,这似乎是它们解决问题的方式。谢谢。 - Brian Ball

10
我要找的是一种获取Lambda表达式中MemberInfo的方法,就好像我在类型参数上使用反射以获取MemberInfo一样。
这不是Lambda表达式转换功能旨在提供的服务。如果您要“标签外”使用某个功能,则可能不会得到所需的结果。
表达式树的目的是以可分析的形式提供编译器对表达式的语义分析,以便在运行时进行分析,而非编译时,从而构建可以远程传输到数据库的查询对象。
编译器的正确语义分析是Name被声明为Base的一个属性,并且在Derived的实例上调用,因此这正是您从生成的表达式树中获得的信息。

4
根据“invoked”,我猜你指的是通过属性进行访问的实际类型。在重新阐述的例子中,childExpMember 的 DeclaredTypeReflectedType 具有相同的类型。MSDN 表示“ReflectedType”会“获取用于获得此 MemberInfo 实例的类对象。”因为该属性是通过“Derived”类型进行访问的,所以“ReflectedType”应该指向它,因为这就是成员的访问方式。我知道实际上没有“Derived”的实例,只是一个表达式,但对我来说这似乎很奇怪。 - Brian Ball

8
很好的问题。我使用一些其他的名称来使它更清晰。
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?

在调试时,您会发现childExpMember.ReflectedTypeBase,而childMember.ReflectedTypeDerived。据我所知,DeclaringType显示成员声明的位置,而ReflectedType显示成员反映到的位置(因为继承/覆盖等原因)。因此,我认为这是一个错误(没有官方确认)。


我可以理解parentMember != childMember,尽管这有点令人惊讶,因为Derived没有以任何方式覆盖基本属性。如果我在一个被广泛使用的框架中发现了一个错误,那既很酷又很糟糕。哪里是提交此错误并让微软的某个人验证的最佳位置?另外,我将采用您的代码并更新我的问题,这样更容易阅读。 - Brian Ball

2
我认为你需要将标志“FlattenHierarchy”传递到GetMember的bindingAttr参数中。
来自MSDN:
指定应返回层次结构中的公共和受保护的静态成员。不返回继承类中的私有静态成员。静态成员包括字段、方法、事件和属性。不返回嵌套类型。

1
好主意,但我尝试了示例代码,结果还是一样的。 - Brian Ball

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接