2013-02-03 6 views
5

Come posso ottenere in modo riflessivo la proprietà che ha DataMember con un nome specifico (assumiamo che ogni DataMember abbia un nome univoco)? Ad esempio, nel seguente codice della proprietà con il DataMember che ha nome "p1" è PropertyOne:Come ottenere la proprietà con DataMemberAttribute con un nome specificato?

[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; } 
} 

Attualmente, ho:

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(); 

questo funziona, ma ci si sente come potrebbe essere migliorata. In particolare, non mi piace che lo GetCustomAttributes() venga chiamato due volte.

Come può essere riscritto meglio? Idealmente, sarebbe bello se potessi farne una semplice copertina.

+0

Sarebbe più efficace per filtrare first out i membri che non hanno un 'DataMemberAttribute' a tutti, e caricare solo i dati degli attributi per coloro che averlo. Utilizzare il [metodo statico '' Attribute.IsDefined'] (http://msdn.microsoft.com/en-us/library/2fdf7hf1.aspx "pagina di riferimento MSDN") per questo scopo ... È più efficiente di 'GetCustomAttribute' . – stakx

risposta

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"); 

Note:

  • Attribute.IsDefined viene utilizzato per verificare la presenza di un attributo personalizzato senza recuperare i suoi dati. Pertanto è più efficiente di Attribute.GetCustomAttribute e utilizzato per saltare le proprietà in un primo passaggio.

  • Dopo che l'operatore Where, si sono lasciati con proprietà che hanno esattamente unDataMemberAttribute: Proprietà senza questo attributo sono stati filtrati, e non può essere applicato più di una volta. Pertanto, possiamo utilizzare Attribute.GetCustomAttribute anziché Attribute.GetCustomAttributes.

2

Si potrebbe usare LINQ:

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(); 

o se si preferisce:

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

si potrebbe usare Fasterflect per rendere il codice riflessione più semplice e facile per gli occhi:

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

Se hai solo bisogno di verificare la presenza dell'attributo, qualcosa di simile potrebbe essere usato al posto:

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

Fasterflect viene fornito con una bella serie di metodi di estensione e include alcune ottimizzazioni delle prestazioni ordinate utilizzando generazione IL se hai bisogno anche di velocità.

1

avevo bisogno per ottenere il valore della proprietà e non la struttura in sé così abituati Darin Dimitrov's answer ma ha aggiunto .GetValue(this) al fine di restituire il valore invece.

Ecco quello che la mia classe ha finito per assomigliare:

[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); 
    } 
}