La risposta accettata descrive correttamente come dovrebbe essere dichiarato l'elenco ed è altamente raccomandata per la maggior parte degli scenari.
Ma mi sono imbattuto in uno scenario diverso, che copre anche la domanda posta. Cosa succede se devi usare un elenco di oggetti esistente, come ViewData["htmlAttributes"]
in MVC ? Come puoi accedere alle sue proprietà (di solito vengono create tramite new { @style="width: 100px", ... }
)?
Per questo scenario leggermente diverso voglio condividere con voi quello che ho scoperto. Nelle soluzioni seguenti, presumo la seguente dichiarazione per nodes
:
List<object> nodes = new List<object>();
nodes.Add(
new
{
Checked = false,
depth = 1,
id = "div_1"
});
1. Soluzione con dinamico
In C # 4.0 e versioni successive , puoi semplicemente trasmettere a dinamico e scrivere:
if (nodes.Any(n => ((dynamic)n).Checked == false))
Console.WriteLine("found not checked element!");
Nota: questo utilizza l' associazione tardiva, il che significa che riconoscerà solo in fase di esecuzione se l'oggetto non ha una Checked
proprietà e RuntimeBinderException
in questo caso genera un - quindi se provi a utilizzare una Checked2
proprietà non esistente riceverai il seguente messaggio a runtime: "'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
.
2. Soluzione con riflessione
La soluzione con la reflection funziona sia con la vecchia che con la nuova versione del compilatore C # . Per le vecchie versioni C # si prega di considerare il suggerimento alla fine di questa risposta.
sfondo
Come punto di partenza, ho trovato una buona risposta qui . L'idea è convertire il tipo di dati anonimo in un dizionario utilizzando la riflessione. Il dizionario lo rende facile accedere alle proprietà, dal momento che i loro nomi vengono memorizzati come chiavi (è possibile accedere come loro myDict["myProperty"]
).
Ispirato dal codice nel link qui sopra, ho creato una classe di un'estensione che fornisce GetProp
, UnanonymizeProperties
e UnanonymizeListItems
come metodi di estensione, che l'accesso alle proprietà Semplificare anonimi. Con questa classe puoi semplicemente eseguire la query come segue:
if (nodes.UnanonymizeListItems().Any(n => (bool)n["Checked"] == false))
{
Console.WriteLine("found not checked element!");
}
oppure puoi usare l'espressione nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
come if
condizione, che filtra in modo implicito e quindi controlla se ci sono elementi restituiti.
Per ottenere il primo oggetto contenente la proprietà "Checked" e restituire la sua proprietà "depth", puoi utilizzare:
var depth = nodes.UnanonymizeListItems()
?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");
o più breve: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
Nota: se hai un elenco di oggetti che non contengono necessariamente tutte le proprietà (ad esempio, alcuni non contengono la proprietà "Controllato") e desideri comunque creare una query basata sui valori "Selezionato", puoi Fai questo:
if (nodes.UnanonymizeListItems(x => { var y = ((bool?)x.GetProp("Checked", true));
return y.HasValue && y.Value == false;}).Any())
{
Console.WriteLine("found not checked element!");
}
Ciò impedisce che si KeyNotFoundException
verifichi se la proprietà "Checked" non esiste.
La classe seguente contiene i seguenti metodi di estensione:
UnanonymizeProperties
: Viene utilizzato per de-anonimizzare le proprietà contenute in un oggetto. Questo metodo utilizza la riflessione. Converte l'oggetto in un dizionario contenente le proprietà e i suoi valori.
UnanonymizeListItems
: Viene utilizzato per convertire un elenco di oggetti in un elenco di dizionari contenenti le proprietà. Può facoltativamente contenere un'espressione lambda da filtrare in anticipo.
GetProp
: Viene utilizzato per restituire un singolo valore corrispondente al nome della proprietà specificato. Consente di trattare le proprietà non esistenti come valori nulli (true) anziché come KeyNotFoundException (false)
Per gli esempi precedenti, tutto ciò che è richiesto è aggiungere la classe di estensione di seguito:
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;
}
}
}
Suggerimento: Il codice di cui sopra è utilizzando il Null-condizionale operatori, disponibili dal C # versione 6.0 - Se si lavora con più vecchio C # compilatori (ad esempio, C # 3.0), è sufficiente sostituire ?.
da .
e ?[
per [
tutto il mondo, ad esempio,
var depth = nodes.UnanonymizeListItems()
.FirstOrDefault(n => n.Contains("Checked"))["depth"];
Se stai non è costretti ad usare un vecchio compilatore C #, tenerlo come è, perché l'utilizzo nulli-condizionali rende nulla la gestione molto più facile.
Nota: come l'altra soluzione con dinamica, anche questa utilizza l'associazione tardiva, ma in questo caso non si ottiene un'eccezione: semplicemente non troverà l'elemento se si fa riferimento a una proprietà non esistente, purché man mano che mantieni gli operatori condizionali nulli .
Ciò che potrebbe essere utile per alcune applicazioni è che la proprietà è indicata tramite una stringa nella soluzione 2, quindi può essere parametrizzata.