2013-02-03 80 views
5

我該如何反思性地獲取具有給定名稱的DataMember的屬性(讓我們假設每個DataMember都有一個唯一的名稱)?例如,下面的代碼使用具有名稱「P1」的數據成員的屬性是PropertyOne如何獲取具有指定名稱的DataMemberAttribute的屬性?

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

目前,我有:

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

這工作,但感覺像它可能是改進。我特別不喜歡GetCustomAttributes()被調用兩次。

如何重寫更好?理想情況下,如果我能把它作爲一個簡單的單線程,那將是非常棒的。

+0

這將是更有效的先過濾掉沒有'DataMemberAttribute'可言的,只加載屬性數據的那些成員有它。爲此,使用['Attribute.IsDefined'靜態方法](http://msdn.microsoft.com/en-us/library/2fdf7hf1.aspx「MSDN參考頁面」)...它比'GetCustomAttribute'更有效率。 – stakx

回答

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

注:

  • Attribute.IsDefined用於檢查自定義屬性的存在而無需檢索其數據。因此它比Attribute.GetCustomAttribute更有效率,並且在第一步中用於跳過屬性。

  • Where操作後,我們剩下的有特性只有一個DataMemberAttribute:沒有這個屬性的屬性都被過濾掉了,它不能應用於超過一次。因此我們可以使用Attribute.GetCustomAttribute而不是Attribute.GetCustomAttributes

2

你可以使用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(); 

或者如果你喜歡:

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

你可以使用Fasterflect,讓您的反射代碼更簡單,更容易對眼睛:

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

如果你只需要檢查屬性的存在,像這樣的東西可能是代替:

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

Fasterflect配備了一個漂亮的一套擴展的方法和包括使用IL代,如果你還需要一些速度整齊的性能優化。

1

我需要得到的財產的價值,而不是本身,以便使用Darin Dimitrov's answer倒是.GetValue(this)到最後返回值,而不是財產。

這裏是我下課東西看起來像:

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