Si desea una lista de tipos anónimos fuertemente tipada, también deberá hacer que la lista sea un tipo anónimo. La forma más fácil de hacer esto es proyectar una secuencia como una matriz en una lista, por ejemplo
var nodes =(new[]{new{Checked=false,/* etc */}}).ToList();
Entonces podrás acceder a él como:
nodes.Any(n => n.Checked);
Debido a la forma en que funciona el compilador, lo siguiente también debería funcionar una vez que haya creado la lista, ya que los tipos anónimos tienen la misma estructura, por lo que también son del mismo tipo. Sin embargo, no tengo un compilador a mano para verificar esto.
Si está almacenando el objeto como tipo object, debe usar la reflexión. Esto es cierto para cualquier tipo de objeto, anónimo u otro. En un objeto o, puede obtener su tipo:
Type t = o.GetType();
Luego de eso buscas una propiedad:
PropertyInfo p = t.GetProperty("Foo");
Entonces de eso puedes obtener un valor:
object v = p.GetValue(o,null);
Esta respuesta está muy atrasada para una actualización de C # 4:
dynamic d = o;object v = d.Foo;
Y ahora otra alternativa en C # 6:
object v = o?.GetType().GetProperty("Foo")?.GetValue(o,null);
¡Tenga en cuenta que al usarlo ?.hacemos que el resultado vesté nullen tres situaciones diferentes!
oes decir null, no hay ningún objeto
ono es nullpero no tiene una propiedadFoo
otiene una propiedad Foopero su valor real resulta ser null.
Por lo tanto, esto no es equivalente a los ejemplos anteriores, pero puede tener sentido si desea tratar los tres casos de la misma manera.
Nunca antes había usado una dinámica hasta ahora, buena actualización para .NET 4.0
Alan
en la solución c # 4 obtendrá una excepción de tiempo de ejecución si la propiedad no existe ( object v = d.Foo), mientras GetValue(o, null)que será nula si no existe.
YaakovHatam
1
No, GetPropertyvolverá nully GetValuelanzará si se pasa eso null, por lo que el efecto general es una excepción. La versión C # 4.0 ofrece una excepción más descriptiva.
Daniel Earwicker
44
Si está utilizando dinámico en un ensamblaje diferente al de la fuente, entonces debe usar [InternalsVisibleTo]
Sarath
2
@DanielEarwicker gracias por completar. También se aplica a los tipos anónimos. Como todas las propiedades generadas para los tipos anónimos son internas.
Sarath
13
Puede iterar sobre las propiedades del tipo anónimo usando Reflection; vea si hay una propiedad "marcada" y si la hay, obtenga su valor.
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!");}}
Si solo necesita una propiedad y ya conoce su nombre, no tiene sentido revisarlas todas; solo use GetProperty y GetValue. Además, System.out.println es Java, no C # ...
Chris Charabaruk
¡Vaya, así es, Chris! Un poco embarazoso ... arreglado ahora.
Glennkentwell
6
La respuesta aceptada describe correctamente cómo debe declararse la lista y es muy recomendable para la mayoría de los escenarios.
Pero me encontré con un escenario diferente, que también cubre la pregunta formulada. ¿Qué sucede si tiene que usar una lista de objetos existente, como ViewData["htmlAttributes"]en MVC ? ¿Cómo puede acceder a sus propiedades (generalmente se crean a través de new { @style="width: 100px", ... })?
Para este escenario ligeramente diferente, quiero compartir con ustedes lo que descubrí. En las siguientes soluciones, estoy asumiendo la siguiente declaración para nodes:
List<object> nodes =newList<object>();
nodes.Add(new{Checked=false,
depth =1,
id ="div_1"});
1. Solución con dinámica
En C # 4.0 y versiones superiores , simplemente puede transmitir a dinámico y escribir:
if(nodes.Any(n =>((dynamic)n).Checked==false))Console.WriteLine("found not checked element!");
Nota: Esto está utilizando el enlace tardío, lo que significa que reconocerá solo en tiempo de ejecución si el objeto no tiene una Checkedpropiedad y arroja un RuntimeBinderExceptionen este caso, por lo que si intenta usar una Checked2propiedad no existente , recibirá el siguiente mensaje en tiempo de ejecución:"'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'" .
2. Solución con reflexión.
La solución con reflexión funciona tanto con las versiones antiguas como con las nuevas del compilador de C # . Para versiones anteriores de C #, tenga en cuenta la sugerencia al final de esta respuesta.
Antecedentes
Como punto de partida, encontré una buena respuesta aquí . La idea es convertir el tipo de datos anónimos en un diccionario utilizando la reflexión. El diccionario facilita el acceso a las propiedades, ya que sus nombres se almacenan como claves (puede acceder a ellas como myDict["myProperty"]).
Inspirado por el código en el enlace anterior, creé una clase de extensión que proporciona GetProp, UnanonymizePropertiesy UnanonymizeListItemscomo métodos de extensión, que simplifican el acceso a propiedades anónimas. Con esta clase, simplemente puede hacer la consulta de la siguiente manera:
if(nodes.UnanonymizeListItems().Any(n =>(bool)n["Checked"]==false)){Console.WriteLine("found not checked element!");}
o puede usar la expresión nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()como ifcondición, que filtra implícitamente y luego verifica si hay algún elemento devuelto.
Para obtener el primer objeto que contiene la propiedad "Comprobada" y devolver su propiedad "profundidad", puede usar:
var depth = nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");
o más corto: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
Nota: Si tiene una lista de objetos que no necesariamente contienen todas las propiedades (por ejemplo, algunos no contienen la propiedad "Comprobada") y aún desea crear una consulta basada en los valores "Comprobados", puede hacer esto:
if(nodes.UnanonymizeListItems(x =>{var y =((bool?)x.GetProp("Checked",true));return y.HasValue&& y.Value==false;}).Any()){Console.WriteLine("found not checked element!");}
Esto evita que se KeyNotFoundExceptionproduzca si la propiedad "Comprobada" no existe.
La siguiente clase contiene los siguientes métodos de extensión:
UnanonymizeProperties: Se utiliza para anonimizar las propiedades contenidas en un objeto. Este método usa la reflexión. Convierte el objeto en un diccionario que contiene las propiedades y sus valores.
UnanonymizeListItems: Se utiliza para convertir una lista de objetos en una lista de diccionarios que contienen las propiedades. Opcionalmente puede contener una expresión lambda para filtrar de antemano.
GetProp: Se utiliza para devolver un valor único que coincida con el nombre de propiedad dado. Permite tratar las propiedades no existentes como valores nulos (verdadero) en lugar de como KeyNotFoundException (falso)
Para los ejemplos anteriores, todo lo que se requiere es que agregue la clase de extensión a continuación:
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;}}}
Sugerencia: El código anterior está utilizando los operadores condicional nulo , disponibles desde la versión 6.0 de C #: si está trabajando con compiladores de C # más antiguos (por ejemplo, C # 3.0), simplemente reemplácelos ?.por .y ?[en [todas partes, por ejemplo
var depth = nodes.UnanonymizeListItems().FirstOrDefault(n => n.Contains("Checked"))["depth"];
Si estás no ve obligado a utilizar un compilador de C # mayor, mantenerlo lo es, porque el uso de los condicionales nulos hace nula manipulación mucho más fácil.
Nota: Al igual que la otra solución con dinámica, esta solución también está utilizando el enlace tardío, pero en este caso no obtendrá una excepción: simplemente no encontrará el elemento si se refiere a una propiedad no existente, siempre que mientras mantiene los operadores condicional nulo .
Lo que podría ser útil para algunas aplicaciones es que se hace referencia a la propiedad a través de una cadena en la solución 2, por lo tanto, se puede parametrizar.
object v = d.Foo
), mientrasGetValue(o, null)
que será nula si no existe.GetProperty
volveránull
yGetValue
lanzará si se pasa esonull
, por lo que el efecto general es una excepción. La versión C # 4.0 ofrece una excepción más descriptiva.Puede iterar sobre las propiedades del tipo anónimo usando Reflection; vea si hay una propiedad "marcada" y si la hay, obtenga su valor.
Vea esta publicación de blog: http://blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx
Entonces algo como:
fuente
La respuesta aceptada describe correctamente cómo debe declararse la lista y es muy recomendable para la mayoría de los escenarios.
Pero me encontré con un escenario diferente, que también cubre la pregunta formulada. ¿Qué sucede si tiene que usar una lista de objetos existente, como
ViewData["htmlAttributes"]
en MVC ? ¿Cómo puede acceder a sus propiedades (generalmente se crean a través denew { @style="width: 100px", ... }
)?Para este escenario ligeramente diferente, quiero compartir con ustedes lo que descubrí. En las siguientes soluciones, estoy asumiendo la siguiente declaración para
nodes
:1. Solución con dinámica
En C # 4.0 y versiones superiores , simplemente puede transmitir a dinámico y escribir:
Nota: Esto está utilizando el enlace tardío, lo que significa que reconocerá solo en tiempo de ejecución si el objeto no tiene una
Checked
propiedad y arroja unRuntimeBinderException
en este caso, por lo que si intenta usar unaChecked2
propiedad no existente , recibirá el siguiente mensaje en tiempo de ejecución:"'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
.2. Solución con reflexión.
La solución con reflexión funciona tanto con las versiones antiguas como con las nuevas del compilador de C # . Para versiones anteriores de C #, tenga en cuenta la sugerencia al final de esta respuesta.
Antecedentes
Como punto de partida, encontré una buena respuesta aquí . La idea es convertir el tipo de datos anónimos en un diccionario utilizando la reflexión. El diccionario facilita el acceso a las propiedades, ya que sus nombres se almacenan como claves (puede acceder a ellas como
myDict["myProperty"]
).Inspirado por el código en el enlace anterior, creé una clase de extensión que proporciona
GetProp
,UnanonymizeProperties
yUnanonymizeListItems
como métodos de extensión, que simplifican el acceso a propiedades anónimas. Con esta clase, simplemente puede hacer la consulta de la siguiente manera:o puede usar la expresión
nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
comoif
condición, que filtra implícitamente y luego verifica si hay algún elemento devuelto.Para obtener el primer objeto que contiene la propiedad "Comprobada" y devolver su propiedad "profundidad", puede usar:
o más corto:
nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
Nota: Si tiene una lista de objetos que no necesariamente contienen todas las propiedades (por ejemplo, algunos no contienen la propiedad "Comprobada") y aún desea crear una consulta basada en los valores "Comprobados", puede hacer esto:
Esto evita que se
KeyNotFoundException
produzca si la propiedad "Comprobada" no existe.La siguiente clase contiene los siguientes métodos de extensión:
UnanonymizeProperties
: Se utiliza para anonimizar las propiedades contenidas en un objeto. Este método usa la reflexión. Convierte el objeto en un diccionario que contiene las propiedades y sus valores.UnanonymizeListItems
: Se utiliza para convertir una lista de objetos en una lista de diccionarios que contienen las propiedades. Opcionalmente puede contener una expresión lambda para filtrar de antemano.GetProp
: Se utiliza para devolver un valor único que coincida con el nombre de propiedad dado. Permite tratar las propiedades no existentes como valores nulos (verdadero) en lugar de como KeyNotFoundException (falso)Para los ejemplos anteriores, todo lo que se requiere es que agregue la clase de extensión a continuación:
Sugerencia: El código anterior está utilizando los operadores condicional nulo , disponibles desde la versión 6.0 de C #: si está trabajando con compiladores de C # más antiguos (por ejemplo, C # 3.0), simplemente reemplácelos
?.
por.
y?[
en[
todas partes, por ejemploSi estás no ve obligado a utilizar un compilador de C # mayor, mantenerlo lo es, porque el uso de los condicionales nulos hace nula manipulación mucho más fácil.
Nota: Al igual que la otra solución con dinámica, esta solución también está utilizando el enlace tardío, pero en este caso no obtendrá una excepción: simplemente no encontrará el elemento si se refiere a una propiedad no existente, siempre que mientras mantiene los operadores condicional nulo .
Lo que podría ser útil para algunas aplicaciones es que se hace referencia a la propiedad a través de una cadena en la solución 2, por lo tanto, se puede parametrizar.
fuente
Recientemente, tuve el mismo problema dentro de .NET 3.5 (no hay dinámica disponible). Así es como lo resolví:
Adaptado de algún lugar en stackoverflow:
Ahora recupera el objeto a través de Cast:
fuente