2012-01-06 2 views
2

内部に宣言されているクラスがあります。さまざまな注釈で装飾されています。特に、[DisplayName( "My Display Name")]アノテーションがあります。私は値を取得するいくつかのコードを持っていますが、クラスがpublic宣言されている場合にのみ動作します。私はリフレクションを使うのは初めてのことです。私は、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#プログラムを作成することによって、かなり簡単に、あなたはこれをテストすることができ、それをダウンロードしてくださいin LinkPAD

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

私が試したときに動作します。あなたがそれを試して何がうまくいかない?また、コードをインクルードしたのは良いことですが、最小限のコンパイル*の例が役立ちます。 – AakashM

+0

2つの部分クラスがあります。1つは多くの属性を持ち、1つは生成されたクラスで、もう1つはメタデータクラスです。私は、生成されたクラスの属性と内部クラスの属性には到達できません。このプロジェクトは巨大なものなので、私はそのような小さな例を作ることができませんでした。私はそれを最も基本的なコンポーネントに焼き付けようとしました。 – ewahner

+0

ええ、それは私が知っているトリッキーです。しかし、「内部」と「公的」の間に行動の違いがあることを考えると、*アセンブリ*分割線がどこにあるかを本当に知る必要があります。物事が立てば、あなたが作っている唯一の* reflection *呼び出しは 'GetCustomAttributes'であり、' BindingFlags'パラメータはありません。 – AakashM

答えて

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.FNameDisplayNameAttributeを探しますが、1つではないので停止します。あなたが必要な属性が見つからない場合には、何をする必要

Metadataという名前のネストされたクラスの

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

ルックは - ここで注意私たちはBindingFlags.NonPublic

を使用する一つの場所であります
// Try and get a nested metadata class 
    var metadataType = typeof(T) 
     .GetNestedType("Metadata", 
         BindingFlags.Public | BindingFlags.NonPublic); 

我々は1見つけた場合:Tのメンバーのための

if (metadataType != null) 
    { 

ルック彼は同じもともと(再びBindingFlags.NonPublic)の話されていたように、1つがある場合は、

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

に名前を付け、あなたのヘルパーメソッドを使用しますが、今回はそれをメタデータ型のメンバを渡す:

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

(I」あなたは"Metadata"文字列は、あなたの代わりに属性を使用して、宣言何かができることを見つける方法を不快に応じ

)ここでは、最終的なNULLかどうかのチェックを省略しましだけでなく、制御フローを閉じる:

  • は(あなたは作品変更することができます)SpGetProfileInfoResultに行くクラスレベルの属性を持つtypeofを使用して、そのMetadataを指して(これはthe approach taken by System.ComponentModelである)、または
  • が点灯し、クラスレベルの属性を持っていますMetadataは、「私はメタデータタイプです」と主張しました。次に、ネストされたクラスという名前のという名前の固定ストリングを検索する代わりに、この特定の属性を持つネストされたクラスを検索します。
+0

よく考えて答えました。おそらく私の解決策よりも少しエレガントかもしれませんが、同じ回答が出てきたようです。 – ewahner

0

私はあなたのコードをデバッグしようとしません私が慣れていないクラスをいくつか使っています。

私が知っていることは、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

1

私はハックを思いついた。私はそこに誰かがこれを少しきれいにするのを助けることができると確信していますが、これは私が作品を見つけたものです。 DeclaringTypeに "Metadata"ネストされたクラスを追加して、メンバーのコレクションを返すその結果に対して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; 
} 
関連する問題