A resposta aceita descreve corretamente como a lista deve ser declarada e é altamente recomendada para a maioria dos cenários.
Mas me deparei com um cenário diferente, que também cobre a pergunta. E se você precisar usar uma lista de objetos existente, como ViewData["htmlAttributes"]
no MVC ? Como você pode acessar suas propriedades (geralmente são criadas via new { @style="width: 100px", ... }
)?
Para esse cenário um pouco diferente, quero compartilhar com você o que descobri. Nas soluções abaixo, estou assumindo a seguinte declaração para nodes
:
List<object> nodes = new List<object>();
nodes.Add(
new
{
Checked = false,
depth = 1,
id = "div_1"
});
1. Solução com dinâmica
No C # 4.0 e versões superiores , você pode simplesmente converter para dinâmico e escrever:
if (nodes.Any(n => ((dynamic)n).Checked == false))
Console.WriteLine("found not checked element!");
Nota: Isso está usando ligação tardia, o que significa que ele reconhecerá apenas em tempo de execução se o objeto não tiver uma Checked
propriedade e dispara um RuntimeBinderException
nesse caso - portanto, se você tentar usar uma Checked2
propriedade não existente , receberá a seguinte mensagem em tempo de execução: "'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
.
2. Solução com reflexão
A solução com reflexão funciona com versões antigas e novas do compilador C # . Para versões antigas do C #, considere a dica no final desta resposta.
fundo
Como ponto de partida, encontrei uma boa resposta aqui . A idéia é converter o tipo de dados anônimos em um dicionário usando reflexão. O dicionário facilita o acesso às propriedades, pois seus nomes são armazenados como chaves (você pode acessá-las como myDict["myProperty"]
).
Inspirado pelo código no link acima, eu criei uma classe de extensão fornecendo GetProp
, UnanonymizeProperties
e UnanonymizeListItems
como métodos de extensão, que o acesso simplificar a propriedades anónimos. Com essa classe, você pode simplesmente fazer a consulta da seguinte maneira:
if (nodes.UnanonymizeListItems().Any(n => (bool)n["Checked"] == false))
{
Console.WriteLine("found not checked element!");
}
ou você pode usar a expressão nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
como if
condição, que filtra implicitamente e depois verifica se há algum elemento retornado.
Para obter o primeiro objeto que contém a propriedade "Marcado" e retornar sua propriedade "profundidade", você pode usar:
var depth = nodes.UnanonymizeListItems()
?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");
ou mais curto: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
Nota: Se você possui uma lista de objetos que não contêm necessariamente todas as propriedades (por exemplo, alguns não contêm a propriedade "Verificado") e ainda deseja criar uma consulta com base nos valores "Verificados", é possível faça isso:
if (nodes.UnanonymizeListItems(x => { var y = ((bool?)x.GetProp("Checked", true));
return y.HasValue && y.Value == false;}).Any())
{
Console.WriteLine("found not checked element!");
}
Isso impede que isso KeyNotFoundException
ocorra se a propriedade "Marcado" não existir.
A classe abaixo contém os seguintes métodos de extensão:
UnanonymizeProperties
: É usado para cancelar o anonimato das propriedades contidas em um objeto. Este método usa reflexão. Ele converte o objeto em um dicionário que contém as propriedades e seus valores.
UnanonymizeListItems
: É usado para converter uma lista de objetos em uma lista de dicionários que contêm as propriedades. Opcionalmente, pode conter uma expressão lambda para filtrar previamente.
GetProp
: É usado para retornar um valor único que corresponde ao nome da propriedade. Permite tratar propriedades não existentes como valores nulos (true) e não como KeyNotFoundException (false)
Para os exemplos acima, tudo o que é necessário é adicionar a classe de extensão abaixo:
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;
}
}
}
Dica: O código acima está usando os nulos-condicional operadores, disponível desde C # versão 6.0 - Se você está trabalhando com compiladores mais antigos C # (por exemplo, C # 3.0), simplesmente substituir ?.
por .
e ?[
por [
toda a parte, por exemplo,
var depth = nodes.UnanonymizeListItems()
.FirstOrDefault(n => n.Contains("Checked"))["depth"];
Se você não for forçado a usar um compilador C # mais antigo, mantenha-o como está, pois o uso de condições nulas facilita muito o tratamento nulo.
Nota: Como a outra solução com dinâmico, esta solução também está usando ligação tardia, mas neste caso você não está recebendo uma exceção - ele simplesmente não encontrará o elemento se estiver se referindo a uma propriedade não existente, desde enquanto você mantém os operadores condicionais nulos .
O que pode ser útil para algumas aplicações é que a propriedade é referida por meio de uma string na solução 2, portanto, pode ser parametrizada.