Если вам нужен строго типизированный список анонимных типов, вам также необходимо сделать этот список анонимным. Самый простой способ сделать это - спроецировать последовательность, такую как массив, в список, например
var nodes =(new[]{new{Checked=false,/* etc */}}).ToList();
Тогда вы сможете получить к нему доступ, например:
nodes.Any(n => n.Checked);
Из-за того, как работает компилятор, следующее должно также работать после создания списка, потому что анонимные типы имеют одинаковую структуру, поэтому они также являются одним и тем же типом. Однако у меня нет компилятора, чтобы это проверить.
Если вы храните объект как тип 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.
Таким образом, это не эквивалентно предыдущим примерам, но может иметь смысл, если вы хотите рассматривать все три случая одинаково.
Никогда раньше не использовал динамический, хорошее обновление для .NET 4.0
Алан,
в решении C # 4 вы получите исключение времени выполнения, если свойство не существует ( object v = d.Foo), а GetValue(o, null)если оно не существует , будет иметь значение null.
YaakovHatam
1
Нет, GetPropertyвернется null, а GetValueесли он будет передан, будет выброшено null, поэтому общий эффект является исключением. Версия C # 4.0 дает более наглядное исключение.
Дэниел Эрвикер,
4
Если вы используете динамическую сборку, отличную от исходной, вам необходимо использовать [InternalsVisibleTo]
Сарат
2
@DanielEarwicker благодарит за завершение. Это также относится к анонимным типам. Поскольку все свойства, созданные для анонимных типов, являются внутренними.
Сарат
13
Вы можете перебирать свойства анонимного типа с помощью Reflection; посмотрите, есть ли свойство "Проверено", и если оно есть, получите его значение.
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!");}}
Если вам нужно только одно свойство, и вы уже знаете его имя, нет смысла просматривать их все; просто используйте GetProperty и GetValue. Кроме того, System.out.println - это Java, а не C # ...
Крис Чарабарук
Ой, это так, Крис! Немного неловко ... исправлено.
glennkentwell
6
Принятый ответ правильно описывает, как следует объявлять список, и настоятельно рекомендуется для большинства сценариев.
Но я столкнулся с другим сценарием, который также охватывает заданный вопрос. Что делать, если вам нужно использовать существующий список объектов, как ViewData["htmlAttributes"]в MVC ? Как получить доступ к его свойствам (обычно они создаются через new { @style="width: 100px", ... })?
Для этого немного другого сценария я хочу поделиться с вами тем, что я обнаружил. В приведенных ниже решениях я предполагаю следующее объявление для nodes:
List<object> nodes =newList<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свойства и RuntimeBinderExceptionв этом случае выдает a - поэтому, если вы попытаетесь использовать несуществующее Checked2свойство, вы получите следующее сообщение на во время выполнения:"'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'" .
2. Решение с отражением
Решение с отражением работает как со старыми, так и с новыми версиями компилятора C # . Для старых версий C # обратите внимание на подсказку в конце этого ответа.
Задний план
В качестве отправной точки, я нашел хороший ответ здесь . Идея состоит в том, чтобы преобразовать анонимный тип данных в словарь с помощью отражения. Словарь упрощает доступ к свойствам, так как их имена хранятся в виде ключей (вы можете получить к ним доступ, например myDict["myProperty"]).
Вдохновленный кода в ссылке выше, я создал класс расширения , обеспечивающий GetProp, UnanonymizePropertiesи UnanonymizeListItemsкак методы расширения, которые упрощают доступ к анонимным свойствам. С помощью этого класса вы можете просто выполнить запрос следующим образом:
if(nodes.UnanonymizeListItems().Any(n =>(bool)n["Checked"]==false)){Console.WriteLine("found not checked element!");}
или вы можете использовать выражение nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()как ifусловие, которое неявно фильтрует, а затем проверяет, возвращаются ли какие-либо элементы.
Чтобы получить первый объект, содержащий свойство «Проверено», и вернуть его свойство «глубина», вы можете использовать:
var depth = nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");
или короче: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
Примечание. Если у вас есть список объектов, которые не обязательно содержат все свойства (например, некоторые не содержат свойство «Проверено»), и вы все же хотите создать запрос на основе значений «Проверено», вы можете сделай это:
if(nodes.UnanonymizeListItems(x =>{var y =((bool?)x.GetProp("Checked",true));return y.HasValue&& y.Value==false;}).Any()){Console.WriteLine("found not checked element!");}
Это предотвращает возникновение KeyNotFoundExceptionошибки, если свойство «Проверено» не существует.
Приведенный ниже класс содержит следующие методы расширения:
UnanonymizeProperties: Используется для деанонимизации свойств, содержащихся в объекте. Этот метод использует отражение. Он преобразует объект в словарь, содержащий свойства и их значения.
UnanonymizeListItems: Используется для преобразования списка объектов в список словарей, содержащих свойства. Он может дополнительно содержать лямбда-выражение для предварительной фильтрации .
GetProp: Используется для возврата одного значения, соответствующего заданному имени свойства. Позволяет рассматривать несуществующие свойства как нулевые значения (true), а не как KeyNotFoundException (false)
Для приведенных выше примеров все, что требуется, - это добавить класс расширения ниже:
publicstaticclassAnonymousTypeExtensions{// makes properties of object accessible publicstaticIDictionaryUnanonymizeProperties(thisobject obj){Type type = obj?.GetType();var properties = type?.GetProperties()?.Select(n => n.Name)?.ToDictionary(k => k, k => type.GetProperty(k).GetValue(obj,null));return properties;}// converts object list into list of properties that meet the filterCriteriapublicstaticList<IDictionary>UnanonymizeListItems(thisList<object> objectList,Func<IDictionary<string,object>,bool> filterCriteria=default){var accessibleList =newList<IDictionary>();foreach(object obj in objectList){var props = obj.UnanonymizeProperties();if(filterCriteria ==default|| filterCriteria((IDictionary<string,object>)props)==true){ accessibleList.Add(props);}}return accessibleList;}// returns specific property, i.e. obj.GetProp(propertyName)// requires prior usage of AccessListItems and selection of one element, because// object needs to be a IDictionary<string, object>publicstaticobjectGetProp(thisobject obj,string propertyName,bool treatNotFoundAsNull =false){try{return((System.Collections.Generic.IDictionary<string,object>)obj)?[propertyName];}catch(KeyNotFoundException){if(treatNotFoundAsNull)returndefault(object);elsethrow;}}}
Подсказка: Приведенный выше код использует нуль-условные операторы, доступные со C # версии 6.0 - если вы работаете с составителями старше C # (например , C # 3.0), просто заменить ?.на .и ?[на [везде, например ,
var depth = nodes.UnanonymizeListItems().FirstOrDefault(n => n.Contains("Checked"))["depth"];
Если вы не обязаны использовать старый компилятор C #, оставьте его как есть, потому что использование условных выражений NULL значительно упрощает обработку NULL.
Примечание. Как и в другом решении с динамикой, это решение также использует позднее связывание, но в этом случае вы не получите исключения - оно просто не найдет элемент, если вы ссылаетесь на несуществующее свойство, пока поскольку вы сохраняете условные операторы NULL .
Что может быть полезно для некоторых приложений, так это то, что свойство упоминается через строку в решении 2, поэтому оно может быть параметризовано.
object v = d.Foo
), аGetValue(o, null)
если оно не существует , будет иметь значение null.GetProperty
вернетсяnull
, аGetValue
если он будет передан, будет выброшеноnull
, поэтому общий эффект является исключением. Версия C # 4.0 дает более наглядное исключение.Вы можете перебирать свойства анонимного типа с помощью Reflection; посмотрите, есть ли свойство "Проверено", и если оно есть, получите его значение.
См. Это сообщение в блоге: http://blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx
Так что-то вроде:
источник
Принятый ответ правильно описывает, как следует объявлять список, и настоятельно рекомендуется для большинства сценариев.
Но я столкнулся с другим сценарием, который также охватывает заданный вопрос. Что делать, если вам нужно использовать существующий список объектов, как
ViewData["htmlAttributes"]
в MVC ? Как получить доступ к его свойствам (обычно они создаются черезnew { @style="width: 100px", ... }
)?Для этого немного другого сценария я хочу поделиться с вами тем, что я обнаружил. В приведенных ниже решениях я предполагаю следующее объявление для
nodes
:1. Решение с динамическим
В C # 4.0 и более поздних версиях вы можете просто привести к динамическому преобразованию и написать:
Примечание: здесь используется позднее связывание, что означает , что он распознает только во время выполнения, если объект не имеет
Checked
свойства иRuntimeBinderException
в этом случае выдает a - поэтому, если вы попытаетесь использовать несуществующееChecked2
свойство, вы получите следующее сообщение на во время выполнения:"'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
.2. Решение с отражением
Решение с отражением работает как со старыми, так и с новыми версиями компилятора C # . Для старых версий C # обратите внимание на подсказку в конце этого ответа.
Задний план
В качестве отправной точки, я нашел хороший ответ здесь . Идея состоит в том, чтобы преобразовать анонимный тип данных в словарь с помощью отражения. Словарь упрощает доступ к свойствам, так как их имена хранятся в виде ключей (вы можете получить к ним доступ, например
myDict["myProperty"]
).Вдохновленный кода в ссылке выше, я создал класс расширения , обеспечивающий
GetProp
,UnanonymizeProperties
иUnanonymizeListItems
как методы расширения, которые упрощают доступ к анонимным свойствам. С помощью этого класса вы можете просто выполнить запрос следующим образом:или вы можете использовать выражение
nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
какif
условие, которое неявно фильтрует, а затем проверяет, возвращаются ли какие-либо элементы.Чтобы получить первый объект, содержащий свойство «Проверено», и вернуть его свойство «глубина», вы можете использовать:
или короче:
nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
Примечание. Если у вас есть список объектов, которые не обязательно содержат все свойства (например, некоторые не содержат свойство «Проверено»), и вы все же хотите создать запрос на основе значений «Проверено», вы можете сделай это:
Это предотвращает возникновение
KeyNotFoundException
ошибки, если свойство «Проверено» не существует.Приведенный ниже класс содержит следующие методы расширения:
UnanonymizeProperties
: Используется для деанонимизации свойств, содержащихся в объекте. Этот метод использует отражение. Он преобразует объект в словарь, содержащий свойства и их значения.UnanonymizeListItems
: Используется для преобразования списка объектов в список словарей, содержащих свойства. Он может дополнительно содержать лямбда-выражение для предварительной фильтрации .GetProp
: Используется для возврата одного значения, соответствующего заданному имени свойства. Позволяет рассматривать несуществующие свойства как нулевые значения (true), а не как KeyNotFoundException (false)Для приведенных выше примеров все, что требуется, - это добавить класс расширения ниже:
Подсказка: Приведенный выше код использует нуль-условные операторы, доступные со C # версии 6.0 - если вы работаете с составителями старше C # (например , C # 3.0), просто заменить
?.
на.
и?[
на[
везде, например ,Если вы не обязаны использовать старый компилятор C #, оставьте его как есть, потому что использование условных выражений NULL значительно упрощает обработку NULL.
Примечание. Как и в другом решении с динамикой, это решение также использует позднее связывание, но в этом случае вы не получите исключения - оно просто не найдет элемент, если вы ссылаетесь на несуществующее свойство, пока поскольку вы сохраняете условные операторы NULL .
Что может быть полезно для некоторых приложений, так это то, что свойство упоминается через строку в решении 2, поэтому оно может быть параметризовано.
источник
Недавно у меня была такая же проблема в .NET 3.5 (динамический доступ отсутствует). Вот как я решил:
Адаптировано откуда-то в stackoverflow:
Теперь вернем объект через приведение:
источник