Принятый ответ правильно описывает порядок объявления списка и настоятельно рекомендуется для большинства сценариев.
Но я столкнулся с другим сценарием, который также охватывает заданный вопрос.
Что если вам нужно использовать существующий список объектов, например 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
и добавляет RuntimeBinderException
в в этом случае - поэтому, если вы попытаетесь использовать несуществующее свойство 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)
Для приведенных выше примеров все, что требуется, это добавить следующий класс расширения:
public static class AnonymousTypeExtensions
{
// makes properties of object accessible
public static IDictionary UnanonymizeProperties(this object 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 filterCriteria
public static List<IDictionary> UnanonymizeListItems(this List<object> objectList,
Func<IDictionary<string, object>, bool> filterCriteria=default)
{
var accessibleList = new List<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>
public static object GetProp(this object obj, string propertyName,
bool treatNotFoundAsNull = false)
{
try
{
return ((System.Collections.Generic.IDictionary<string, object>)obj)
?[propertyName];
}
catch (KeyNotFoundException)
{
if (treatNotFoundAsNull) return default(object); else throw;
}
}
}
Подсказка: Приведенный выше код использует операторы null-conditional , доступные с C # версии 6.0 - если вы работаете с более старыми компиляторами C # (например, C # 3.0 ), просто замените ?.
на .
и ?[
на [
везде, например
var depth = nodes.UnanonymizeListItems()
.FirstOrDefault(n => n.Contains("Checked"))["depth"];
Если вы не вынуждены использовать более старый компилятор C #, оставьте его как есть, потому что использование нулевых условий значительно упрощает обработку нуля.
Примечание: Как и другое решение с динамическим, это решение также использует позднюю привязку, но в этом случае вы не получите исключение - оно просто не найдет элемент, если вы ссылаетесь к несуществующему свойству, если вы сохраняете нулевые условные операторы .
Что может быть полезно для некоторых приложений, так это то, что свойство упоминается через строку в решении 2, поэтому его можно параметризировать.