Ich muss alle Eigenschaften mithilfe von Reflection in der Reihenfolge abrufen, in der sie in der Klasse deklariert sind. Laut MSDN kann die Bestellung bei Verwendung nicht garantiert werdenGetProperties()
Die GetProperties-Methode gibt keine Eigenschaften in einer bestimmten Reihenfolge zurück, z. B. in alphabetischer Reihenfolge oder in Deklarationsreihenfolge.
Aber ich habe gelesen, dass es eine Problemumgehung gibt, indem die Eigenschaften nach dem sortiert werden MetadataToken
. Meine Frage ist also, ist das sicher? Ich kann anscheinend keine Informationen auf MSDN darüber finden. Oder gibt es eine andere Möglichkeit, dieses Problem zu lösen?
Meine aktuelle Implementierung sieht wie folgt aus:
var props = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.OrderBy(x => x.MetadataToken);
c#
reflection
properties
getproperties
Magnus
quelle
quelle
Antworten:
Auf .net 4.5 (und sogar .net 4.0 in vs2012) können Sie mit Reflexion viel besser arbeiten, indem Sie einen cleveren Trick mit
[CallerLineNumber]
Attribut verwenden, bei dem der Compiler die Reihenfolge für Sie in Ihre Eigenschaften einfügt :[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; } }
Und dann Reflexion verwenden:
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) { // }
Wenn Sie sich mit Teilklassen befassen müssen, können Sie die Eigenschaften zusätzlich mit sortieren
[CallerFilePath]
.quelle
CsvColumnAttribute
und dies als StandardwertFieldIndex
verwendenWenn Sie die Attributroute wählen, ist hier eine Methode, die ich in der Vergangenheit verwendet habe.
public static IOrderedEnumerable<PropertyInfo> GetSortedProperties<T>() { return typeof(T) .GetProperties() .OrderBy(p => ((Order)p.GetCustomAttributes(typeof(Order), false)[0]).Order); }
Dann benutze es so;
var test = new TestRecord { A = 1, B = 2, C = 3 }; foreach (var prop in GetSortedProperties<TestRecord>()) { Console.WriteLine(prop.GetValue(test, null)); }
Wo;
class TestRecord { [Order(1)] public int A { get; set; } [Order(2)] public int B { get; set; } [Order(3)] public int C { get; set; } }
Die Methode wird gesperrt, wenn Sie sie auf einem Typ ohne vergleichbare Attribute für alle Ihre Eigenschaften ausführen. Seien Sie also vorsichtig, wie sie verwendet wird, und sie sollte für die Anforderungen ausreichen.
Ich habe die Definition von Ordnung: Attribut weggelassen, da Yahias Link zu Marc Gravells Beitrag ein gutes Beispiel enthält.
quelle
Laut MSDN
MetadataToken
ist innerhalb eines Moduls einzigartig - es gibt nichts zu sagen, dass es überhaupt eine Bestellung garantiert.AUCH wenn es sich so verhält, wie Sie es möchten, wäre dies implementierungsspezifisch und könnte sich jederzeit ohne vorherige Ankündigung ändern.
Siehe diesen alten MSDN-Blogeintrag .
Ich würde dringend empfehlen, sich von jeglichen Abhängigkeiten von solchen Implementierungsdetails fernzuhalten - siehe diese Antwort von Marc Gravell .
Wenn Sie zur Kompilierungszeit etwas benötigen, können Sie sich Roslyn ansehen (obwohl es sich in einem sehr frühen Stadium befindet).
quelle
Was ich beim Sortieren nach MetadataToken getestet habe, funktioniert.
Einige Benutzer hier behaupten, dies sei irgendwie kein guter Ansatz / nicht zuverlässig, aber ich habe noch keine Beweise dafür gesehen - vielleicht können Sie hier ein Code-Snipet posten, wenn der angegebene Ansatz nicht funktioniert?
Informationen zur Abwärtskompatibilität - während Sie jetzt an Ihrem .net 4 / .net 4.5 arbeiten - Microsoft stellt .net 5 oder höher her, sodass Sie davon ausgehen können, dass diese Sortiermethode in Zukunft nicht mehr funktioniert.
Natürlich werden Sie vielleicht bis 2017, wenn Sie auf .net9 upgraden, eine Kompatibilitätsunterbrechung erleben, aber bis dahin werden Microsoft-Leute wahrscheinlich den "offiziellen Sortiermechanismus" herausfinden. Es macht keinen Sinn, zurück zu gehen oder Dinge zu zerbrechen.
Das Spielen mit zusätzlichen Attributen für die Eigenschaftsreihenfolge erfordert auch Zeit und Implementierung - warum sollte man sich die Mühe machen, wenn die MetadataToken-Sortierung funktioniert?
quelle
GetProperties()
. Ihr Argument, warum Sie sich auf die Reihenfolge verlassen können, ist genau das gleiche wie das Argument, warum Sie sich nicht auf zukünftige Versionen von .net verlassen können, die dieses Verhalten nicht ändern: Jede neue Version kann die Implementierungsdetails ändern. Nun folgt .net tatsächlich der Philosophie, dass „Bugs Features sind“, sodass sie in Wirklichkeit wahrscheinlich nie die Reihenfolge von ändern würdenGetProperties()
. Es ist nur so, dass die API sagt, dass sie das dürfen.Sie können DisplayAttribute in System.Component.DataAnnotations anstelle eines benutzerdefinierten Attributs verwenden. Ihre Anforderung hat sowieso etwas mit Anzeige zu tun.
quelle
Wenn Sie mit der zusätzlichen Abhängigkeit zufrieden sind, können Sie dies mit Protobuf-Net von Marc Gravell tun, ohne sich Gedanken darüber machen zu müssen, wie Reflektion und Caching am besten implementiert werden können. Dekorieren Sie Ihre Felder einfach mit
[ProtoMember]
und greifen Sie dann in numerischer Reihenfolge auf die Felder zu:MetaType metaData = ProtoBuf.Meta.RuntimeTypeModel.Default[typeof(YourTypeName)]; metaData.GetFields();
quelle
Ich habe es so gemacht:
internal static IEnumerable<Tuple<int,Type>> TypeHierarchy(this Type type) { var ct = type; var cl = 0; while (ct != null) { yield return new Tuple<int, Type>(cl,ct); ct = ct.BaseType; cl++; } } internal class PropertyInfoComparer : EqualityComparer<PropertyInfo> { public override bool Equals(PropertyInfo x, PropertyInfo y) { var equals= x.Name.Equals(y.Name); return equals; } public override int GetHashCode(PropertyInfo obj) { return obj.Name.GetHashCode(); } } internal static IEnumerable<PropertyInfo> GetRLPMembers(this Type type) { return type .TypeHierarchy() .SelectMany(t => t.Item2 .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(prop => Attribute.IsDefined(prop, typeof(RLPAttribute))) .Select( pi=>new Tuple<int,PropertyInfo>(t.Item1,pi) ) ) .OrderByDescending(t => t.Item1) .ThenBy(t => t.Item2.GetCustomAttribute<RLPAttribute>().Order) .Select(p=>p.Item2) .Distinct(new PropertyInfoComparer()); }
mit der Eigenschaft wie folgt deklariert:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class RLPAttribute : Attribute { private readonly int order_; public RLPAttribute([CallerLineNumber]int order = 0) { order_ = order; } public int Order { get { return order_; } } }
quelle
Aufbauend auf der oben akzeptierten Lösung können Sie so etwas verwenden, um den genauen Index zu erhalten
Gegeben
public class MyClass { [Order] public string String1 { get; set; } [Order] public string String2 { get; set; } [Order] public string String3 { get; set; } [Order] public string String4 { get; set; } }
Erweiterungen
public static class Extensions { public static int GetOrder<T,TProp>(this T Class, Expression<Func<T,TProp>> propertySelector) { var body = (MemberExpression)propertySelector.Body; var propertyInfo = (PropertyInfo)body.Member; return propertyInfo.Order<T>(); } public static int Order<T>(this PropertyInfo propertyInfo) { return typeof(T).GetProperties() .Where(property => Attribute.IsDefined(property, typeof(OrderAttribute))) .OrderBy(property => property.GetCustomAttributes<OrderAttribute>().Single().Order) .ToList() .IndexOf(propertyInfo); } }
Verwendung
var myClass = new MyClass(); var index = myClass.GetOrder(c => c.String2);
Beachten Sie , dass es keine Fehlerprüfung oder Fehlertoleranz gibt. Sie können nach Belieben Pfeffer und Salz hinzufügen
quelle
Eine andere Möglichkeit ist die Nutzung der
System.ComponentModel.DataAnnotations.DisplayAttribute
Order
Eigenschaft. Da es eingebaut ist, muss kein neues spezifisches Attribut erstellt werden.Wählen Sie dann solche geordneten Eigenschaften aus
const int defaultOrder = 10000; var properties = type.GetProperties().OrderBy(p => p.FirstAttribute<DisplayAttribute>()?.GetOrder() ?? defaultOrder).ToArray();
Und Klasse kann so präsentiert werden
public class Toto { [Display(Name = "Identifier", Order = 2) public int Id { get; set; } [Display(Name = "Description", Order = 1) public string Label {get; set; } }
quelle