Как получить доступ к свойству анонимного типа В C#?
у меня есть это:
List<object> nodes = new List<object>();
nodes.Add(
new {
Checked = false,
depth = 1,
id = "div_" + d.Id
});
... и мне интересно, могу ли я затем захватить "проверенное" свойство анонимного объекта. Я не уверен, что это вообще возможно. Пробовал делать это:
if (nodes.Any(n => n["Checked"] == false))
... но это не работает.
спасибо
4 ответа:
Если вам нужен строго типизированный список анонимных типов, вам также нужно сделать список анонимным типом. Самый простой способ сделать это-спроецировать последовательность, такую как массив, в список, например
var nodes = (new[] { new { Checked = false, /* etc */ } }).ToList();
тогда вы сможете получить к нему доступ, как:
nodes.Any(n => n.Checked);
из-за того, как работает компилятор, следующее должно также работать после создания списка, потому что анонимные типы имеют одинаковую структуру, поэтому они также являются одним и тем же типом. Я не есть компилятор, чтобы проверить это, хотя.
nodes.Add(new { Checked = false, /* etc */ });
если вы храните объект как тип
object
, вы должны использовать отражение. Это верно для любого типа объекта, анонимного или иного. На объекте o вы можете получить его тип:Type t = o.GetType();
затем из этого вы ищете свойство:
PropertyInfo p = t.GetProperty("Foo");
тогда из этого вы можете получить значение:
object v = p.GetValue(o, null);
этот ответ давно назрел для обновления для C# 4:
dynamic d = o; object v = d.Foo;
а теперь еще одна альтернатива в C# 6:
object v = o?.GetType().GetProperty("Foo")?.GetValue(o, null);
Примечание. что с помощью
?.
мы вызываем результирующийv
наnull
в трех разных ситуациях!
o
иnull
, так что нет никакого объекта вообщеo
неnull
, но не имеет имуществаFoo
o
свойствоFoo
но его реальная ценность оказываетсяnull
.таким образом, это не эквивалентно предыдущим примерам, но может иметь смысл, если вы хотите рассматривать все три случая тот же.
вы можете перебирать свойства анонимного типа с помощью отражения; посмотрите, есть ли свойство "проверено", и если есть, то получите его значение.
смотрите этот пост в блоге: http://blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx
что-то вроде:
foreach(object o in nodes) { Type t = o.GetType(); PropertyInfo[] pi = t.GetProperties(); foreach (PropertyInfo p in pi) { if (p.Name=="Checked" && !(bool)p.GetValue(o)) Console.WriteLine("awesome!"); } }
принятый ответ правильно описывает, как список должен быть объявлен и настоятельно рекомендуется для большинства сценариев.
но я столкнулся с другим сценарием, который также охватывает заданный вопрос. Что делать, если вам нужно использовать существующий список объектов, например
ViewData["htmlAttributes"]
в MVC? Как вы можете получить доступ к его свойствам (они обычно создаются черезnew { @style="width: 100px", ... }
)?для этого немного другой сценарий я хочу поделиться с вами тем, что я узнал. В приведенных ниже решениях я предполагаю следующее объявление для
nodes
:List<object> nodes = new List<object>(); nodes.Add( new { Checked = false, depth = 1, id = "div_1" });
1. Решение с динамическим
в C# 4.0 и более поздних версиях вы можете просто привести к динамическому и написать:
if (nodes.Any(n => ((dynamic)n).Checked == false)) Console.WriteLine("found not checked element!");
Примечание: это с помощью позднего связывания, что означает, что он будет распознавать только во время выполнения, если объект не имеет
Checked
собственность и бросает!--8--> в этом случае - так что если вы пытаетесь использовать несуществующуюChecked2
свойство вы получите следующее сообщение во время выполнения:"'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
.2. Решение с отражением
фон
в качестве отправной точки, я нашел хороший ответ здесь. Идея в том, чтобы преобразовать анонимный тип данных в словарь с помощью отражения.
вдохновленный кодом в ссылке выше, я создал класс расширения, который упрощает доступ к анонимным свойствам. С помощью этого класса вы можете просто сделать запрос в следует:
if (nodes.AccessListItems().Any(n => (bool)n["Checked"] == false)) { Console.WriteLine("found not checked element!"); }
все, что требуется, это добавить класс расширений ниже:
// simplifies access to anonymous properties public static class AnonymousTypeExtensions { // make properties of object accessible // eg. x.AccessProperties() or x.AccessProperties()["PropName"] public static IDictionary AccessProperties(this object o, string propertyName=null) { Type type = o?.GetType(); var properties = type?.GetProperties() ?.Select(n => n.Name) ?.ToDictionary(k => k, k => type.GetProperty(k).GetValue(o, null)); return properties; } // returns specific property, i.e. x.AccessProperty(propertyName) public static object AccessProperty(this object o, string propertyName) { return o?.AccessProperties()?[propertyName]; } // converts object list into list of properties public static List<IDictionary> AccessListItems(this List<object> objectList) { var accessibleList = new List<IDictionary>(); foreach (object obj in objectList) { accessibleList.Add(obj.AccessProperties()); } return accessibleList; } }
приведенный выше код использует null-conditional операторы, доступные с C# версии 6.0-если вы работаете со старыми компиляторами, просто замените
?.
by.
и?[
by[
. В противном случае держите его как есть, потому что это делает обработку null намного проще.Примечание: как и другое решение с динамическим является это решение также использует позднюю привязку, но в этом случае вы не получаете исключения - он просто не найдет элемент, если вы ссылаетесь на несуществующее свойство. Что может быть полезно для некоторых приложений, так это то, что свойство упоминается через строку.