2013-02-03 12 views
5

Wie kann ich die Eigenschaft, die das DataMember hat, reflektiv mit einem bestimmten Namen abrufen (nehmen wir an, dass jedes DataMember einen eindeutigen Namen hat)? Zum Beispiel in dem folgenden Code die Eigenschaft mit dem Datamember, der Name „p1“ hat PropertyOne ist:Wie erhält man die Eigenschaft, die ein DataMemberAttribute mit einem angegebenen Namen hat?

[DataContract(Name = "MyContract")] 
public class MyContract 
{ 
    [DataMember(Name = "p1")] 
    public string PropertyOne { get; set; } 

    [DataMember(Name = "p2")] 
    public string PropertyTwo { get; set; } 

    [DataMember(Name = "p3")] 
    public string PropertyThree { get; set; } 
} 

Derzeit habe ich:

string dataMemberName = ...; 

var dataMemberProperties = typeof(T).GetProperties().Where(p => p.GetCustomAttributes(typeof(DataMemberAttribute), false).Any()); 

var propInfo = dataMemberProperties.Where(p => ((DataMemberAttribute)p.GetCustomAttributes(typeof(DataMemberAttribute), false).First()).Name == dataMemberName).FirstOrDefault(); 

Dies funktioniert, aber es fühlt sich an wie es sein könnte verbessert. Besonders gefällt mir nicht, dass GetCustomAttributes() zweimal aufgerufen wird.

Wie kann es besser umgeschrieben werden? Im Idealfall wäre es toll, wenn ich es zu einem einfachen One-Liner machen könnte.

+0

Es wäre effizienter, zuerst Mitglieder herauszufiltern, die nicht eine 'DataMemberAttribute' gar nicht installiert haben, und nur die Attributdaten für die Last, die habe es. Verwenden Sie dazu die statische Methode '' Attribute.IsDefined' '(http://msdn.microsoft.com/en-us/library/2fdf7hf1.aspx "MSDN-Referenzseite"). Dies ist effizienter als 'GetCustomAttribute' . – stakx

Antwort

9
// using System.Linq; 
// using System.Reflection; 
// using System.Runtime.Serialization; 
obj.GetType() 
    .GetProperties(…) 
    .Where(p => Attribute.IsDefined(p, typeof(DataMemberAttribute))) 
    .Single(p => ((DataMemberAttribute)Attribute.GetCustomAttribute(
        p, typeof(DataMemberAttribute))).Name == "Foo"); 

Anmerkungen:

  • Attribute.IsDefined wird verwendet für das Vorhandensein eines benutzerdefinierten Attributs zu überprüfen, ohne seine Daten abzurufen. Somit ist es effizienter als Attribute.GetCustomAttribute und verwendet, um Eigenschaften in einem ersten Schritt zu überspringen.

  • Nach dem Where Betreiber sind wir mit Eigenschaften, die links genau einDataMemberAttribute haben: Eigenschaften ohne dieses Attribut herausgefiltert wurden, und es kann nicht mehr als einmal angewendet werden. Daher können wir Attribute.GetCustomAttribute anstelle von Attribute.GetCustomAttributes verwenden.

2

Sie können LINQ verwenden:

string dataMemberName = ...; 
var propInfo = 
    (from property in typeof(T).GetProperties() 
    let attributes = property 
     .GetCustomAttributes(typeof(DataMemberAttribute), false) 
     .OfType<DataMemberAttribute>() 
    where attributes.Any(a => a.Name == dataMemberName) 
    select property).FirstOrDefault(); 

oder wenn Sie es vorziehen:

string dataMemberName = ...; 
var propInfo = typeof(T) 
    .GetProperties() 
    .Where(p => p 
     .GetCustomAttributes(typeof(DataMemberAttribute), false) 
     .OfType<DataMemberAttribute>() 
     .Any(x => x.Name == dataMemberName) 
    ) 
    .FirstOrDefault(); 
1

Sie Fasterflect verwenden könnte Ihre Reflexion Code einfacher und angenehmer für die Augen zu machen:

var property = typeof(T).MembersAndAttributes(MemberTypes.Property, typeof(DataMemberAttribute)) 
    .Where(ma => ma.Attributes.First().Name == dataMemberName) 
    .Select(ma => ma.Member as PropertyInfo) 
    .FirstOrDefault(); 

Wenn Sie nur auf das Vorhandensein des Attributs prüfen müssen, könnte so etwas sein

var property = typeof(T).PropertiesWith<DataMemberAttribute>(Flags.InstancePublic) 
    .Where(p => p.Name == dataMemberName).FirstOrDefault(); 

Fasterflect kommt mit einem schönen Satz von Erweiterungsmethoden und enthält einige ordentlichen Performance-Optimierungen unter Verwendung von IL-Generation, wenn Sie auch Geschwindigkeit benötigen stattdessen verwendet.

1

I benötigt, um den Wert der Immobilie und nicht die Immobilie selbst so Darin Dimitrov's answer aber .GetValue(this) am Ende hinzugefügt verwendet bekommen den Wert stattdessen zurückzukehren. Hier

ist, was meine Klasse endete aussehen wie:

[DataContract] 
public class Item 
{ 
    [DataMember(Name = "kpiId")] 
    public string KPIId { get; set; } 
    [DataMember(Name = "value")] 
    public string Value { get; set; } 
    [DataMember(Name = "unit")] 
    public string Unit{ get; set; } 
    [DataMember(Name = "status")] 
    public string Status { get; set; } 
    [DataMember(Name = "category")] 
    public string Category { get; set; } 
    [DataMember(Name = "description")] 
    public string Description { get; set; } 
    [DataMember(Name = "source")] 
    public string Source { get; set; } 
    [DataMember(Name = "messages")] 
    public SysMessage[] Messages { get; set; } 

    public object getDataMemberByName(string name) 
    { 
     return (typeof(Item).GetProperties().FirstOrDefault(p => p.GetCustomAttributes(typeof(DataMemberAttribute), false) 
           .OfType<DataMemberAttribute>() 
           .Any(x => x.Name == name))).GetValue(this); 
    } 
}