Лямбда-выражение не возвращает ожидаемого MemberInfo

Я столкнулся с проблемой, которую я не ожидал. Пример, вероятно, проиллюстрирует мой вопрос лучше, чем абзац:

ОБНОВЛЕНО: Перейти к последнему блоку кода для более красноречивого примера кода.

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 может определить свойство с тем же именем с thew new оператор, но в этом случае я не сделал.

Второй действительно самый удивительный для меня (и это суть моей проблемы). Несмотря на то, что параметр лямбды четко определен как тип C, он все равно возвращает его, как если бы к свойству обращались из базового класса.

То, что я ищу, - это способ получить MemberInfo из лямбда-выражения, как если бы я использовал отражение в типе параметра для получения MemberInfo. Мой проект по существу хранит MemberInfos в своего рода словаре, и он должен иметь функциональность, позволяющую получить доступ к элементам, предоставив лямбда-выражение.

Пересмотренный пример кода Дэнни Ченом

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 ответа

Решение

Возьмите тип (первый) параметр выражения и скажите

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

То, что я ищу, - это способ получить MemberInfo из лямбда-выражения, как если бы я использовал отражение в типе параметра для получения MemberInfo.

Это не услуга, которую должны обеспечивать преобразования дерева выражений в лямбдах. Если вы собираетесь использовать функцию "off label", вы можете не получить желаемых результатов.

Цель деревьев выражений состоит в том, чтобы предложить семантический анализ компилятора выражения в форме, пригодной для анализа во время выполнения, а не во время компиляции с целью создания объектов запроса, которые можно переместить в базы данных.

Правильный семантический анализ компилятора заключается в том, что Name объявляется как свойство Base и вызывается для экземпляра Derived, так что это именно та информация, которую вы получаете из результирующего дерева выражений.

Хороший вопрос Я использую некоторые другие имена, чтобы сделать это более понятным.

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.ReflectedType является Base, в то время как childMember.ReflectedType является Derived, насколько мне известно DeclaringType показывает, где член объявлен, а ReflectedType показывает, где член отражается (из-за наследования / переопределения / и т.д.). Так что я думаю, что это ошибка (нет официального подтверждения).

I believe you need to pass the flag "FlattenHierarchy" into the bindingAttr parameter of GetMember.

Из MSDN:

Указывает, что открытые и защищенные статические члены в иерархии должны быть возвращены. Private static members in inherited classes are not returned. Статические члены включают поля, методы, события и свойства. Nested types are not returned.

Другие вопросы по тегам