Лямбда-выражение не возвращает ожидаемого 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.