2012-01-06 30 views
2

我有一個聲明爲Internal的類。它裝飾着各種註釋。特別是[DisplayName(「我的顯示名稱」)]註釋。我有一些代碼將檢索的價值,但只有在該類聲明爲公共的時纔有效。我對使用反射有點新鮮。我相信我需要指定BindingFlags.NonPublic被使用,但我不知道在哪裏。從內部類獲取DisplayNameAttribute

LinqPAD代碼:

void Main() 
{ 
    List<SpGetProfileInfoResult> p = new List<SpGetProfileInfoResult>(); 
    p.Add(new SpGetProfileInfoResult() { FName = "Eric" }); 
    p.Add(new SpGetProfileInfoResult() { FName = "Mike" }); 

    p.Dump(); 

    foreach (var item in p) 
    { 
     Console.WriteLine(item.DisplayName(i => i.FName)); 
     Console.WriteLine(item.FName); 
    } 

} 

public partial class SpGetProfileInfoResult 
{ 
    // Uncomment this annotation to see that this part will work 
    // [System.ComponentModel.DisplayNameAttribute("[BILLTO-FNAME]")] 
    public string FName { get; set; } 
} 

public partial class SpGetProfileInfoResult 
{ 
    internal class Metadata 
    { 
     // This attribute is never available seems. 
     [System.ComponentModel.DisplayNameAttribute("[BILL-FNAME]")] 
     public string FName { get; set; } 
    } 
} 

public static class Tag 
{ 
    public static T GetAttribute<T>(this MemberInfo member, bool isRequired) where T : Attribute 
    { 
     var attribute = member.GetCustomAttributes(typeof(T), false).SingleOrDefault(); 

     if (attribute == null && isRequired) 
     { 
      throw new ArgumentException(
       string.Format(
       "The {0} attribute must be defined on member {1}", 
       typeof(T).Name, 
       member.Name)); 
     } 

     return (T)attribute; 
    } 

    public static string DisplayName<T>(this T src,Expression<Func<T, object>> propertyExpression) 
    { 
     Type metadata = null; 

     var memberInfo = GetPropertyInformation(propertyExpression.Body); 
     if (memberInfo == null) 
     { 
      throw new ArgumentException(
       "No property reference expression was found.", 
       "propertyExpression"); 
     } 

     var attr = memberInfo.GetAttribute<DisplayNameAttribute>(false); 
     if (attr == null) 
     { 
      return memberInfo.Name; 
     } 

     return attr.DisplayName; 
    } 

    public static MemberInfo GetPropertyInformation(Expression propertyExpression) 
    { 
     MemberExpression memberExpr = propertyExpression as MemberExpression; 
     if (memberExpr == null) 
     { 
      UnaryExpression unaryExpr = propertyExpression as UnaryExpression; 
      if (unaryExpr != null && unaryExpr.NodeType == ExpressionType.Convert) 
      { 
       memberExpr = unaryExpr.Operand as MemberExpression; 
      } 
     } 

     if (memberExpr != null && memberExpr.Member.MemberType == MemberTypes.Property) 
     { 
      return memberExpr.Member; 
     } 

     return null; 
    } 
} 

用法:

如果你沒有LinqPAD,你應該只創建一個新的C#程序下載它,那麼你可以測試這個很容易地在LinkPAD

Debug.WriteLine(item.DisplayName(i => i.FName)); 
+0

工程,當我嘗試它。當你嘗試時出了什麼問題?另外,你已經包含了代碼是很好的,但是最小的*編譯*例子真的有幫助。 – AakashM 2012-01-09 13:56:43

+0

有兩個部分類,一個具有許多屬性,它是生成的類,然後是上面的類,它是元數據類。我只能得到生成的類的屬性,而沒有任何內部類。該項目非常龐大,所以我無法創建這樣一個小例子。我試圖將其烘焙到最基本的組件上。 – ewahner 2012-01-09 14:58:30

+0

是的,我知道這很棘手。但是鑑於'internal'和'public'之間的行爲有所不同,我們確實需要知道* assembly *分界線在哪裏。按照現狀,唯一的* reflection *調用是'GetCustomAttributes',並且沒有任何'BindingFlags'參數。 – AakashM 2012-01-09 15:31:45

回答

3

所以它看起來像你希望能夠裝飾部分類的現有成員,通過在一個單獨的部分片提供元數據。還有對於沒有內置機制(見例如this question and the classes mentioned in the answer),但如果你願意堅持一個約定,你可以滾你自己:

因此,假設我們在一個局部有

public partial class SpGetProfileInfoResult 
{ 
    public string FName { get; set; } 
} 

片我們不能變化,和

public partial class SpGetProfileInfoResult 
{ 
    internal class Metadata 
    { 
     [System.ComponentModel.DisplayNameAttribute("[BILL-FNAME]")] 
     public string FName { get; set; } 
    } 
} 

在部分片我們可以變化。您已經擁有大部分物品:在DisplayName()中,您成功確定我們正在查看FName財產;然後你在T.FName上尋找DisplayNameAttribute,但是沒有一個,所以這就是停止的地方。

你需要做的是,在你沒有找到你需要的屬性的情況下,

var attr = memberInfo.GetAttribute<DisplayNameAttribute>(false); 
if (attr == null) 
{ 

查找名爲Metadata嵌套類 - 請注意這裏是一個地方,我們用BindingFlags.NonPublic

// Try and get a nested metadata class 
    var metadataType = typeof(T) 
     .GetNestedType("Metadata", 
         BindingFlags.Public | BindingFlags.NonPublic); 

如果我們找到一個:

if (metadataType != null) 
    { 

查找t的成員

 if (membersOnMetadataType.Any()) 
     { 
      var attrOnMetadataType = membersOnMetadataType[0] 
       .GetAttribute<DisplayNameAttribute>(false); 
      return attrOnMetadataType.DisplayName; 

(I」:作爲最初被談論(BindingFlags.NonPublic再次)

 var membersOnMetadataType = metadataType.GetMember(memberInfo.Name, 
      BindingFlags.Instance | 
      BindingFlags.Public | 
      BindingFlags.NonPublic); 

如果有,就用你的輔助方法,但是這一次它傳遞的元數據類型的成員,他同一個名字VE這裏省略最後的無效支票,以及關閉控制流量)

你如何難吃發現"Metadata"字符串,可以轉而做一些聲明與屬性取決於:

  • 有去上SpGetProfileInfoResult類級別屬性(一塊你可以變化),使用typeof(這是the approach taken by System.ComponentModel),或
  • 有去上一個類級別的屬性在其MetadataMetadata,聲稱「我是元數據類型」。然後,而不是搜索名爲的固定字符串的嵌套類,而是搜索具有此特定屬性的嵌套類。
+0

很好想出答案。大概比我的解決方案稍微優雅一點,但看起來我們似乎有同樣的答案。 – ewahner 2012-01-10 13:08:52

0

我不會嘗試調試你的代碼,因爲y你使用一些我不熟悉的類。

我知道的一件事是MemberInfo沒有GetAttribute()函數。您必須在那裏使用擴展方法。

但是我可以告訴你,因爲類型爲internal,你不需要任何特殊的綁定標誌。只有會員的知名度很重要,在這種情況下它是公開的。

using System; 
using System.ComponentModel; 

namespace ConsoleApplication1 
{ 
    internal class Metadata 
    { 
     [DisplayName("[BILL-FNAME]")] 
     public string FName { get; set; } 
    } 

    class Program 
    { 
     static void Main() 
     { 
      var memberInfo = typeof(Metadata).GetMember("FName")[0]; 
      var atrributes = memberInfo.GetCustomAttributes(false); 
      Console.WriteLine(atrributes[0].GetType().Name); 
     } 
    } 
} 

輸出:

DisplayNameAttribute

+0

我更新了我的示例。你對GetAttribute是正確的。我添加了這個功能,所以整個例子都很清楚。我還更新了班級以顯示該工具如何生成班級。由於元數據是內部的,因此我無法以您展示的方式訪問它。 – ewahner 2012-01-09 12:02:27

1

經過這一段時間,我想出了一個黑客。我確信有人可以幫我清理一下,但這是我發現的作品。我必須將「元數據」嵌套類添加到DeclaringType,然後對該結果執行GetMember,該結果返回成員集合。

public static string DisplayName<T>(this T src, Expression<Func<T, object>> propertyExpression) 
{ 
    var memberInfo = GetPropertyInformation(propertyExpression.Body); 
    var mytype = src.GetType(); 
    string strType = mytype.Name + "+Metadata"; 
    var metaType = Type.GetType(strType); 
    MemberInfo[] mem = metaType.GetMember(memberInfo.Name); 
    var att = mem[0].GetCustomAttributes(typeof(DisplayNameAttribute), true).FirstOrDefault() as DisplayNameAttribute; 

    if (att == null) 
     return memberInfo.Name; 
    else 
     return att.DisplayName; 
}