Lambda выражение не возвращает ожидаемый 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 может определять свойство с тем же именем с помощью оператора w 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?

Ответ 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

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

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

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

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

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

Ответ 4

Я считаю, что вам нужно передать флаг "FlattenHierarchy" в параметр bindingAttr GetMember.

Из MSDN:

Указывает, что должны быть возвращены общедоступные и защищенные статические члены в иерархии. Частные статические члены в унаследованных классах не возвращаются. Статические члены включают поля, методы, события и свойства. Вложенные типы не возвращаются.