what - system.reflection c#
Orden de propiedad `Type.GetProperties` (5)
Confiar en un detalle de implementación que está documentado explícitamente como no garantizado es una receta para el desastre.
El ''enfoque recomendado'' variaría dependiendo de lo que quiera hacer con estas propiedades una vez que las tenga. ¿Solo mostrándolos en la pantalla? Los documentos de MSDN se agrupan por tipo de miembro (propiedad, campo, función) y luego se ordenan alfabéticamente dentro de los grupos.
Si su formato de mensaje se basa en el orden de los campos, entonces deberá:
Especifique el orden esperado en algún tipo de definición de mensaje. Los búferes de protocolo de Google funcionan de esta manera si recuerdo: la definición del mensaje se compila en ese caso desde un archivo .proto a un archivo de código para usar en cualquier idioma con el que esté trabajando.
Confíe en un orden que se puede generar de forma independiente, por ejemplo, orden alfabético.
Version corta
La documentación de MSDN para Type.GetProperties
indica que no se garantiza que la colección que devuelve esté en orden alfabético o de declaración, aunque ejecutar una prueba simple muestra que, en general, se devuelve en orden de declaración. ¿Hay escenarios específicos que sepa donde este no es el caso? Más allá de eso, ¿cuál es la alternativa sugerida?
Versión detallada
Me doy cuenta de la documentación de MSDN para los estados de Type.GetProperties
:
El método GetProperties no devuelve propiedades en un orden particular, como orden alfabético o de declaración. Su código no debe depender del orden en que se devuelven las propiedades, porque ese orden varía.
por lo tanto, no hay garantía de que la colección devuelta por el método se ordenará de manera específica. Sobre la base de algunas pruebas, he encontrado que, por el contrario, las propiedades devueltas aparecen en el orden en que están definidas en el tipo.
Ejemplo:
class Simple
{
public int FieldB { get; set; }
public string FieldA { get; set; }
public byte FieldC { get; set; }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Simple Properties:");
foreach (var propInfo in typeof(Simple).GetProperties())
Console.WriteLine("/t{0}", propInfo.Name);
}
}
Salida:
Simple Properties:
FieldB
FieldA
FieldC
Uno de los casos en que esto difiere solo ligeramente es cuando el tipo en cuestión tiene un padre que también tiene propiedades:
class Parent
{
public int ParentFieldB { get; set; }
public string ParentFieldA { get; set; }
public byte ParentFieldC { get; set; }
}
class Child : Parent
{
public int ChildFieldB { get; set; }
public string ChildFieldA { get; set; }
public byte ChildFieldC { get; set; }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Parent Properties:");
foreach (var propInfo in typeof(Parent).GetProperties())
Console.WriteLine("/t{0}", propInfo.Name);
Console.WriteLine("Child Properties:");
foreach (var propInfo in typeof(Child).GetProperties())
Console.WriteLine("/t{0}", propInfo.Name);
}
}
Salida:
Parent Properties:
ParentFieldB
ParentFieldA
ParentFieldC
Child Properties:
ChildFieldB
ChildFieldA
ChildFieldC
ParentFieldB
ParentFieldA
ParentFieldC
Lo que significa que el método GetProperties
recorre la cadena de herencia de abajo hacia arriba cuando descubre las propiedades. Eso está bien y puede ser manejado como tal.
Preguntas:
- ¿Hay situaciones específicas en las que el comportamiento descrito diferiría de lo que me he perdido?
- Si según el orden no se recomienda, ¿cuál es el enfoque recomendado?
Una solución aparentemente obvia sería definir un atributo personalizado que indique el orden en que deben aparecer las propiedades (similar a la propiedad Order
en el atributo DataMember
). Algo como:
public class PropOrderAttribute : Attribute
{
public int SeqNbr { get; set; }
}
Y luego implementar como:
class Simple
{
[PropOrder(SeqNbr = 0)]
public int FieldB { get; set; }
[PropOrder(SeqNbr = 1)]
public string FieldA { get; set; }
[PropOrder(SeqNbr = 2)]
public byte FieldC { get; set; }
}
Pero, como muchos han descubierto, esto se convierte en un grave problema de mantenimiento si su tipo tiene 100 propiedades y necesita agregar una entre las 2 primeras.
ACTUALIZAR
Los ejemplos que se muestran aquí son simplemente para fines demostrativos. En mi escenario específico, defino un formato de mensaje usando una clase, luego recorro las propiedades de la clase y capto sus atributos para ver cómo se debe desmarcar un campo específico en el mensaje. El orden de los campos en el mensaje es significativo, por lo que el orden de las propiedades en mi clase debe ser significativo.
Funciona actualmente solo por iterar sobre la colección de GetProperties
de GetProperties
, pero dado que la documentación indica que no se recomienda, estaba buscando entender por qué y qué otra opción tengo.
El orden simplemente no está garantizado; lo que pasa, pasa.
Casos obvios donde podría cambiar:
- cualquier cosa que implemente ICustomTypeDescriptor
- cualquier cosa con un TypeDescriptionProvider
Pero un caso más sutil: las clases parciales. Si una clase se divide en varios archivos, el orden de su uso no está definido en absoluto. Consulte ¿Se define formalmente el "orden textual" en todas las clases parciales?
Por supuesto, no está definido incluso para una definición única (no parcial); p
Pero imagina
Archivo 1
partial class Foo {
public int A {get;set;}
}
Archivo 2
partial class Foo {
public int B {get;set:}
}
Sin embargo, no hay una orden de declaración formal entre A y B. Consulte la publicación vinculada para ver cómo suele ocurrir.
Re su edición; El mejor enfoque es especificar la información del gerente por separado; un enfoque común sería usar un atributo personalizado que tome un orden numérico y decorar a los miembros con eso. A continuación, puede ordenar en función de este número. protobuf-net hace algo muy similar, y francamente yo sugeriría usar una biblioteca de serialización existente aquí:
[ProtoMember(n)]
public int Foo {get;set;}
Donde "n" es un número entero. En el caso de protobuf-net específicamente, también hay una API para especificar estos números por separado, lo cual es útil cuando el tipo no está bajo su control directo.
Para lo que vale, la clasificación por MetadataToken parece funcionar para mí.
GetType().GetProperties().OrderBy(x => x.MetadataToken)
Artículo original: http://www.sebastienmahe.com/v3/seb.blog/2010/03/08/c-reflection-getproperties-kept-in-declaration-order/
Utilizo los atributos personalizados para agregar los metadatos necesarios (se usa con un servicio similar a REST que consume y devuelve pares de Clave = Valor delimitados por CRLF).
Primero, un atributo personalizado:
class ParameterOrderAttribute : Attribute
{
public int Order { get; private set; }
public ParameterOrderAttribute(int order)
{
Order = order;
}
}
Luego, decora tus clases:
class Response : Message
{
[ParameterOrder(0)]
public int Code { get; set; }
}
class RegionsResponse : Response
{
[ParameterOrder(1)]
public string Regions { get; set; }
}
class HousesResponse : Response
{
public string Houses { get; set; }
}
Un método práctico para convertir un PropertyInfo en un int ordenable:
private int PropertyOrder(PropertyInfo propInfo)
{
int output;
var orderAttr = (ParameterOrderAttribute)propInfo.GetCustomAttributes(typeof(ParameterOrderAttribute), true).SingleOrDefault();
output = orderAttr != null ? orderAttr.Order : Int32.MaxValue;
return output;
}
Aún mejor, escribir es como una extensión:
static class PropertyInfoExtensions
{
private static int PropertyOrder(this PropertyInfo propInfo)
{
int output;
var orderAttr = (ParameterOrderAttribute)propInfo.GetCustomAttributes(typeof(ParameterOrderAttribute), true).SingleOrDefault();
output = orderAttr != null ? orderAttr.Order : Int32.MaxValue;
return output;
}
}
Finalmente, ahora puede consultar su objeto Type con:
var props = from p in type.GetProperties()
where p.CanWrite
orderby p.PropertyOrder() ascending
select p;
1:
He pasado el último día resolviendo un problema en un proyecto MVC 3, y todo se redujo a este problema en particular. Básicamente, se basó en que el orden de las propiedades era el mismo durante toda la sesión, pero en algunas ocasiones algunas de las propiedades cambiaron de lugar, desordenando el sitio.
Primero, el código llamado Type.GetProperties()
para definir nombres de columna en una tabla jqGrid dinámica, algo que en este caso ocurre una vez por page_load
. Posteriormente, se Type.GetProperties()
método Type.GetProperties()
para rellenar los datos reales de la tabla y, en raras ocasiones, las propiedades cambiaron de lugar y desordenaron la presentación por completo. En algunos casos, se cambiaron otras propiedades en las que se basó el sitio para un subgrid jerárquico, es decir, ya no se podían ver los datos secundarios porque la columna de ID contenía datos erróneos. En otras palabras: sí, esto definitivamente puede suceder . Tener cuidado.
2:
Si necesita un orden consistente a lo largo de la sesión del sistema pero no necesariamente el mismo orden para todas las sesiones, la solución es muy sencilla : almacene la matriz PropertyInfo[]
que obtiene de Type.GetProperties()
como un valor en el webcache o en un diccionario con el tipo (o nombre tipográfico) como clave de caché / diccionario. Posteriormente, cuando esté a punto de hacer un Type.GetProperties()
, sustitúyalo por HttpRuntime.Cache.Get(Type/Typename)
o Dictionary.TryGetValue(Type/Typename, out PropertyInfo[])
. De esta manera, tendrá la garantía de obtener siempre el orden que encontró la primera vez.
Si siempre necesita el mismo orden (es decir, para todas las sesiones del sistema) le sugiero que combine el enfoque anterior con algún tipo de mecanismo de configuración, es decir, especifique el orden en web.config / app.config, ordene la matriz PropertyInfo[]
que obtiene de Type.GetProperties()
acuerdo con el orden especificado, y luego almacenarlo en el diccionario caché / estático.