2012-01-30 21 views
49

Necesito obtener todas las propiedades usando la reflexión en el orden en que se declaran en la clase. Según MSDN el orden no puede ser garantizada cuando se utiliza GetProperties()Obtener propiedades en orden de declaración utilizando la reflexión

El método GetProperties no devuelve propiedades en un orden en particular, tales como orden alfabético o declaración.

Pero he leído que hay una solución al ordenar las propiedades por el MetadataToken. Entonces mi pregunta es, ¿es seguro? No puedo encontrar información sobre MSDN al respecto. ¿O hay alguna otra forma de resolver este problema?

Mi implementación actual se ve de la siguiente manera:

var props = typeof(T) 
    .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) 
    .OrderBy(x => x.MetadataToken); 
+12

De todos modos, es una mala idea. Cree su propio atributo con valor de orden o cualquier otro metadato y marque los campos de clase con ese atributo. –

+1

Quizás podría agregar un nuevo atributo que contiene una int del orden. Luego, obtenga las propiedades, obtenga DisplayOrderAttribute de cada propiedad y ordene por eso. – BlueChippy

+1

Por curiosidad, ¿por qué estás haciendo esto, qué estás tratando de lograr? –

Respuesta

12

Según MSDNMetadataToken es único dentro de un módulo - no hay nada diciendo que garantiza cualquier orden en absoluto.

incluso si lo hiciera se comportan de la forma que desee a que sería específico de la implementación y podría cambiar en cualquier momento sin previo aviso.

Ver este viejo MSDN blog entry.

no te recomiendo que se mantenga alejado de cualquier dependeny en tales detalles de implementación - ver this answer from Marc Gravell.

Si necesita algo en tiempo de compilación que podría echar un vistazo a Roslyn (aunque está en una etapa muy temprana).

9

Si va por la ruta del atributo, aquí hay un método que he usado en el pasado;

public static IOrderedEnumerable<PropertyInfo> GetSortedProperties<T>() 
{ 
    return typeof(T) 
    .GetProperties() 
    .OrderBy(p => ((Order)p.GetCustomAttributes(typeof(Order), false)[0]).Order); 
} 

Entonces úsala así;

var test = new TestRecord { A = 1, B = 2, C = 3 }; 

foreach (var prop in GetSortedProperties<TestRecord>()) 
{ 
    Console.WriteLine(prop.GetValue(test, null)); 
} 

Dónde;

class TestRecord 
{ 
    [Order(1)] 
    public int A { get; set; } 

    [Order(2)] 
    public int B { get; set; } 

    [Order(3)] 
    public int C { get; set; } 
} 

El método barf si se ejecuta en un tipo sin atributos comparables sobre todas sus propiedades, obviamente, así que tenga cuidado cómo se utiliza y debe ser suficiente para el requisito.

He dejado de lado la definición de orden: Atributo ya que hay una buena muestra de enlace de Yahia al post de Marc Gravell.

+2

Si se trata de un requisito de visualización, sería apropiado utilizar [DataAnnotations.DisplayAttribute] (http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations .displayattribute.aspx), que tiene un campo de Orden. – Jerph

0

Si está satisfecho con la dependencia adicional, puede usar Marc Gravell's Protobuf-Net para hacerlo sin tener que preocuparse por la mejor manera de implementar la reflexión y el almacenamiento en caché, etc. Simplemente decore sus campos usando [ProtoMember] y luego acceda a los campos en numérico orden usando:

MetaType metaData = ProtoBuf.Meta.RuntimeTypeModel.Default[typeof(YourTypeName)]; 

metaData.GetFields(); 
98

En .net 4.5 (and even .net 4.0 in vs2012) se puede hacer mucho mejor con la reflexión usando truco inteligente con [CallerLineNumber] atributo, dejando fin compilador de inserción en sus propiedades:

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] 
public sealed class OrderAttribute : Attribute 
{ 
    private readonly int order_; 
    public OrderAttribute([CallerLineNumber]int order = 0) 
    { 
     order_ = order; 
    } 

    public int Order { get { return order_; } } 
} 


public class Test 
{ 
    //This sets order_ field to current line number 
    [Order] 
    public int Property2 { get; set; } 

    //This sets order_ field to current line number 
    [Order] 
    public int Property1 { get; set; } 
} 

Y a continuación, utilizar la reflexión:

var properties = from property in typeof(Test).GetProperties() 
       where Attribute.IsDefined(property, typeof(OrderAttribute)) 
       orderby ((OrderAttribute)property.GetCustomAttributes(typeof(OrderAttribute), false).Single()).Order 
       select property; 

foreach (var property in properties) 
{ 

} 

Si usted tiene que tratar con clases parciales, también puede ordenar las propiedades usando [CallerFilePath].

+0

Interesante, gracias! – Magnus

+0

De hecho, una mejor manera, gracias! – Gerard

+2

¡Esto es muy inteligente! Me pregunto si tiene algún contra argumento en contra de esto. Parece ser bastante elegante para mí en realidad. Estoy usando Linq2CSV y creo que voy a heredar de 'CsvColumnAttribute' y lo uso como el valor predeterminado de' FieldIndex' – julealgon

3

Lo que he probado clasificando por MetadataToken funciona.

Algunos de los usuarios aquí afirman que este enfoque no es bueno de alguna manera/no es confiable, pero aún no he visto ninguna evidencia de eso - ¿quizás puede publicar algún snipet de código aquí cuando el enfoque dado no funciona?

Acerca de la compatibilidad con versiones anteriores - mientras está trabajando en .net 4/.net 4.5 - Microsoft está haciendo .net 5 o superior, por lo que puede suponer que este método de clasificación no se romperá en el futuro .

Por supuesto, quizás para el año 2017 cuando vaya a actualizarse a .net9 llegará a la ruptura de compatibilidad, pero para entonces los chicos de Microsoft probablemente descubrirán el "mecanismo de clasificación oficial". No tiene sentido retroceder o romper cosas.

Jugar con atributos adicionales para el ordenamiento de propiedades también requiere tiempo e implementación. ¿Por qué preocuparse si funciona la clasificación de MetadataToken?

1

Puede usar DisplayAttribute en System.Component.DataAnnotations, en lugar de atributo personalizado. Su requisito tiene que hacer algo con la pantalla de todos modos.

Cuestiones relacionadas