Лямбда-выражение не возвращает ожидаемого MemberInfo - PullRequest
21 голосов
/ 12 июля 2011

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

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

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?

Ответы [ 4 ]

16 голосов
/ 12 июля 2011

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

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];
10 голосов
/ 13 июля 2011

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

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

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

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

6 голосов
/ 12 июля 2011

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

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

2 голосов
/ 12 июля 2011

Полагаю, вам нужно передать флаг "FlattenHierarchy" в параметр bindingAttr объекта GetMember.

Из MSDN:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...