2013-02-03 21 views
5

W jaki sposób można uzyskać odbicie właściwości, która ma DataMember o danej nazwie (załóżmy, że każdy DataMember ma unikalną nazwę)? Na przykład w poniższym kodzie własność z DataMember który ma nazwę „P1” jest PropertyOne:Jak uzyskać właściwość, która ma DataMemberAttribute o określonej nazwie?

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

Obecnie mam:

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

To działa, ale wydaje się, jakby to mogło być ulepszony. W szczególności nie podoba mi się, że GetCustomAttributes() jest wywoływana dwa razy.

Jak można go lepiej napisać na nowo? Idealnie byłoby wspaniale, gdybym mógł uczynić to prostym jedno-liniowym.

+0

To byłoby bardziej efektywne, aby najpierw odfiltrować członków, że nie mają 'DataMemberAttribute' w ogóle, a jedynie załadować dane atrybutów dla tych, które posiadać to. Użyj metody statycznej ['Attribute.IsDefined'] (http://msdn.microsoft.com/en-us/library/2fdf7hf1.aspx" strona referencyjna MSDN ") w tym celu ... Jest bardziej wydajna niż" GetCustomAttribute " . – stakx

Odpowiedz

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

Uwagi:

  • Attribute.IsDefined służy do sprawdzania obecności atrybutu niestandardowego bez pobierania jej dane. Dzięki temu jest bardziej wydajny niż Attribute.GetCustomAttribute i służy do pomijania właściwości w pierwszym kroku.

  • Po operatora Where, jesteśmy w lewo z właściwościami, które mają dokładnie jedenDataMemberAttribute: Właściwości bez tego atrybutu zostały odfiltrowane i nie można zastosować więcej niż jeden raz. Dlatego możemy użyć Attribute.GetCustomAttribute zamiast Attribute.GetCustomAttributes.

2

Można użyć 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(); 

lub jeśli wolisz:

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

Można użyć Fasterflect aby Twój kod odbicie prostsze i łatwiejsze w oczy:

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

Jeśli musisz tylko sprawdzić obecność atrybutu, coś takiego może być stosowany zamiast:

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

Fasterflect pochodzi z ładnym zestaw metod rozszerzenie i obejmuje kilka schludne optymalizacje wydajności przy użyciu generacji IL jeśli trzeba także prędkość.

1

Musiałem uzyskać wartość nieruchomości, a nie własność sama tak przyzwyczajeni Darin Dimitrov's answer ale dodał .GetValue(this) do końca, aby zwrócić wartość zamiast.

Oto co moja klasa skończyło się patrząc jak:

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