Принятый ответ правильно описывает, как следует объявлять список, и настоятельно рекомендуется для большинства сценариев.
Но я столкнулся с другим сценарием, который также охватывает заданный вопрос. Что делать, если вам нужно использовать существующий список объектов, как 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
в этом случае выдает 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)
Для приведенных выше примеров все, что требуется, - это добавить класс расширения ниже:
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;
}
}
}
Подсказка: Приведенный выше код использует нуль-условные операторы, доступные со C # версии 6.0 - если вы работаете с составителями старше C # (например , C # 3.0), просто заменить ?.
на .
и ?[
на [
везде, например ,
var depth = nodes.UnanonymizeListItems()
.FirstOrDefault(n => n.Contains("Checked"))["depth"];
Если вы не обязаны использовать старый компилятор C #, оставьте его как есть, потому что использование условных выражений NULL значительно упрощает обработку NULL.
Примечание. Как и в другом решении с динамикой, это решение также использует позднее связывание, но в этом случае вы не получите исключения - оно просто не найдет элемент, если вы ссылаетесь на несуществующее свойство, пока поскольку вы сохраняете условные операторы NULL .
Что может быть полезно для некоторых приложений, так это то, что свойство упоминается через строку в решении 2, поэтому оно может быть параметризовано.