web-dev-qa-db-ja.com

カスタム属性が適用されたメンバーを取得するにはどうすればよいですか?

C#で カスタム属性 を作成していますが、属性がメソッドとプロパティのどちらに適用されるかによって異なることをしたいです。最初は、カスタム属性コンストラクターでnew StackTrace().GetFrame(1).GetMethod()を実行して、どのメソッドが属性コンストラクターを呼び出したかを確認しようとしていましたが、今では何が得られるのかわかりません。属性がプロパティに適用された場合はどうなりますか? GetMethod()はそのプロパティのMethodBaseインスタンスを返しますか? C#で属性が適用されたメンバーを取得する別の方法はありますか?

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property,
    AllowMultiple = true)]
public class MyCustomAttribute : Attribute

更新:さて、間違った質問をしていたかもしれません。カスタム属性クラス内から、カスタム属性が適用されたメンバー(またはメンバーを含むクラス)を取得するにはどうすればよいですか? Aaronaught 私の属性が適用されたクラスメンバを見つけるためにスタックを上って行くことを提案しましたが、属性のコンストラクタ内からこの情報を取得するにはどうすればよいですか?

55
Sarah Vessels

スタックフレームとメソッドがどのように機能するかに関して多くの混乱があるように見えるので、ここに簡単なデモがあります。

static void Main(string[] args)
{
    MyClass c = new MyClass();
    c.Name = "MyTest";
    Console.ReadLine();
}

class MyClass
{
    private string name;

    void TestMethod()
    {
        StackTrace st = new StackTrace();
        StackFrame currentFrame = st.GetFrame(1);
        MethodBase method = currentFrame.GetMethod();
        Console.WriteLine(method.Name);
    }

    public string Name
    {
        get { return name; }
        set
        {
            TestMethod();
            name = value;
        }
    }
}

このプログラムの出力は次のようになります。

set_Name

C#のプロパティは、構文糖の形式です。 ILのゲッターメソッドとセッターメソッドにコンパイルされ、一部の.NET言語がそれらをプロパティとして認識することさえできない可能性があります。プロパティの解決は、慣例により完全に行われ、IL仕様にはルールがありません。

さて、プログラムが独自のスタックを調べたいという本当に正当な理由があったとしましょう(そして貴重な少数実用的な理由があります)。なぜ世界では、プロパティとメソッドに対して異なる動作をしたいのですか?

属性の背後にある理論的根拠は、それらが一種のメタデータであるということです。別の動作が必要な場合は、それをコーディングします属性に。属性がメソッドまたはプロパティのどちらに適用されるかに応じて2つの異なることを意味する場合、2つの属性が必要です。最初のターゲットをAttributeTargets.Methodに設定し、2番目のターゲットをAttributeTargets.Propertyに設定します。シンプル。

しかし、もう一度、呼び出し元のメソッドからいくつかの属性を取得するために独自のスタックを歩くことは、せいぜい危険です。ある意味では、プログラムの設計をフリーズし、だれかが拡張またはリファクタリングすることをはるかに難しくしています。これは、属性が通常使用される方法ではありません。より多くの適切なの例は、検証属性のようなものになります。

public class Customer
{
    [Required]
    public string Name { get; set; }
}

次に、渡される実際のエンティティについて何も知らないバリデータコードでこれを行うことができます。

public void Validate(object o)
{
    Type t = o.GetType();
    foreach (var prop in
        t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
    {
        if (Attribute.IsDefined(prop, typeof(RequiredAttribute)))
        {
            object value = prop.GetValue(o, null);
            if (value == null)
                throw new RequiredFieldException(prop.Name);
        }
    }
}

言い換えれば、あなたはあなたに与えられたインスタンスの属性を調べているが、そのタイプについて必ずしも何も知らない。 XML属性、データコントラクト属性、さらには属性属性-.NET Frameworkのほとんどすべての属性は、このように使用され、インスタンスのタイプに関しては動的ですが、尊重されない機能を実装しますプログラムの状態またはスタックに何が起こるか。スタックトレースを作成する時点で、実際にこれを制御していることはほとんどありません。

したがって、まだ説明していない非常に正当な理由がない限り、do n'tを使用することを再度お勧めします。そうでなければ、あなたは傷ついた世界にいる可能性があります。

絶対に必要な場合(警告しなかったと言わないでください)、2つの属性を使用します。1つはメソッドに適用でき、もう1つはプロパティに適用できます。単一のスーパー属性よりも作業がはるかに簡単であることがわかると思います。

41
Aaronaught

属性はメタデータを提供し、それらが装飾しているもの(クラス、メンバーなど)について何も知りません。一方、装飾されているものは、装飾されている属性を要求できます。

装飾されているもののタイプを知る必要がある場合は、コンストラクターで明示的に属性に渡す必要があります。

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, 
    AllowMultiple = true)] 
public class MyCustomAttribute : Attribute
{
   Type type;

   public MyCustomAttribute(Type type)
   {
      this.type = type;
   }
}
40
Scott Dorman

GetMethodは常に関数名を返します。プロパティの場合、get_PropertyNameまたはset_PropertyName

プロパティは基本的にメソッドの一種であるため、プロパティを実装すると、コンパイラは結果のMSILにget_メソッドとset_メソッドの2つの別個の関数を作成します。これが、スタックトレースでこれらの名前を受け取る理由です。

4
Amirshk

カスタム属性は、属性が適用される場所を表すICustomAttributeProvider(リフレクションオブジェクト)でGetCustomAttributesメソッドを呼び出すコードによってアクティブになります。そのため、プロパティの場合、一部のコードはプロパティのPropertyInfoを取得し、その上でGetCustomAttributesを呼び出します。

検証フレームワークを構築する場合は、カスタム属性の型とメンバーを検査するコードを記述する必要があります。たとえば、検証フレームワークに参加するために属性が実装するインターフェイスを使用できます。次のように簡単にすることができます。

public interface ICustomValidationAttribute
{
    void Attach(ICustomAttributeProvider foundOn);
}

コードは、(たとえば)Typeでこのインターフェイスを検索できます。

var validators = type.GetCustomAttributes(typeof(ICustomValidationAttribute), true);
foreach (ICustomValidationAttribute validator in validators)
{
     validator.Attach(type);
}

(おそらく、反射グラフ全体を歩いて、各ICustomAttributeProviderに対してこれを実行します)。 .net FXでの実際の同様のアプローチの例については、WCFの「動作」(IServiceBehavior、IOperationBehaviorなど)を参照してください。

更新:.net FXにはある程度の汎用性がありますが、基本的にはContextBoundObjectおよびContextAttributeの形式で文書化されていないインターセプトフレームワークです。 AOPでの使用例については、Webで検索できます。

4
alexdej