Lambda 表达式未返回预期的 MemberInfo

Posted

技术标签:

【中文标题】Lambda 表达式未返回预期的 MemberInfo【英文标题】:Lambda expression not returning expected MemberInfo 【发布时间】:2011-10-03 06:33:40 【问题描述】:

我遇到了一个我没想到的问题。一个例子可能比一个段落更能说明我的问题:

更新:跳到最后一个代码块以获得更雄辩的代码示例。

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?

【问题讨论】:

【参考方案1】:

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

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

【讨论】:

这可能是我最终会做的事情,但我希望比实际使用反射更干净一些。如果一段时间后没有我更喜欢的答案,我会将其标记为正确并使用它。 我查看了其他几个具有类似功能的开源项目,他们似乎就是这样解决这个问题的。谢谢。【参考方案2】:

我相信您需要将标志“FlattenHierarchy”传递到GetMember的bindingAttr参数中。

来自 MSDN:

指定应返回层次结构中的公共和受保护的静态成员。不返回继承类中的私有静态成员。静态成员包括字段、方法、事件和属性。不返回嵌套类型。

【讨论】:

好主意,但我用示例代码试了一下,结果是一样的。【参考方案3】:

好问题。为了更清楚,我使用了一些其他名称。

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。 AFAIK DeclaringType 显示成员的声明位置,而ReflectedType 显示成员反映到的位置(由于继承/覆盖/等)。所以我认为这是一个错误(没有官方确认)。

【讨论】:

我可以买那个 parentMember != childMember,虽然这有点令人惊讶,因为 Derived 不会以任何方式覆盖基本属性。如果我在框架的一部分中发现了一个经常使用的错误,那将是很酷的,同时也很糟糕。提交此错误并让 Microsoft 人员验证的最佳地点在哪里?另外,我将使用您的代码并更新我的问题,这样更容易阅读。【参考方案4】:

我正在寻找一种从 lambda 表达式中获取 MemberInfo 的方法,就好像我使用了对参数类型的反射来获取 MemberInfo。

这不是 lambda 表达式树转换旨在提供的服务。 如果您要使用“标签外”功能,那么您可能无法获得想要的结果。

表达式树的目的是以一种可在运行时分析的形式提供编译器对表达式的语义分析,而不是在编译时构建可远程访问数据库的查询对象。

编译器的正确语义分析是 Name 被声明为 Base 的属性并在 Derived 的实例上调用,所以这正是你得到的信息生成的表达式树。

【讨论】:

我猜 invoked 您指的是访问该属性的实际类型。在重述的示例中,childExpMember 对DeclaredType 的类型与对ReflectedType 的类型相同。 MSDN 声明ReflectedType“获取用于获取此 MemberInfo 实例的类对象。”由于该属性是通过Derived 类型访问的,因此ReflectedType 应该指向它,因为这是访问成员的方式。我意识到没有Derived 的实际实例,它只是一个表达式,但对我来说确实很奇怪。

以上是关于Lambda 表达式未返回预期的 MemberInfo的主要内容,如果未能解决你的问题,请参考以下文章

WHERE 子句中的 Lambda 表达式未按预期工作

C++ 11 正则表达式未按预期返回组

正则表达式未按预期进行评估

带有 lambda 表达式的 LINQ where 子句具有 OR 子句和返回不完整结果的空值

Zapier:代码未返回预期的所有值

lambda表达式