web-dev-qa-db-ja.com

カスタム属性のコンストラクターはいつ実行されますか?

いつ実行されますか?適用するオブジェクトごとに実行されますか、それとも一度だけですか?それは何でもできますか、またはそのアクションは制限されていますか?

73

コンストラクターはいつ実行されますか?サンプルで試してください:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Creating MyClass instance");
        MyClass mc = new MyClass();
        Console.WriteLine("Setting value in MyClass instance");
        mc.Value = 1;
        Console.WriteLine("Getting attributes for MyClass type");
        object[] attributes = typeof(MyClass).GetCustomAttributes(true);
    }

}

[AttributeUsage(AttributeTargets.All)]
public class MyAttribute : Attribute
{
    public MyAttribute()
    {
        Console.WriteLine("Running constructor");
    }
}

[MyAttribute]
class MyClass
{
    public int Value { get; set; }
}

そして、出力は何ですか?

Creating MyClass instance
Setting value in MyClass instance
Getting attributes for MyClass type
Running constructor

したがって、属性の検査を開始すると、属性コンストラクターが実行されます。属性は、型のインスタンスではなく、型から取得されることに注意してください。

65
Fredrik Mörk

コンストラクターが実行されます毎回GetCustomAttributesが呼び出されるか、または他のコードがコンストラクターを直接呼び出すたびに(実行する正当な理由があるわけではありませんが、不可能でもありません)。

少なくとも.NET 4.0では、属性インスタンスはキャッシュされないであることに注意してください。 GetCustomAttributesが呼び出されるたびに新しいインスタンスが構築されます:

[Test]
class Program
{
    public static int SomeValue;

    [Test]
    public static void Main(string[] args)
    {
        var method = typeof(Program).GetMethod("Main");
        var type = typeof(Program);

        SomeValue = 1;

        Console.WriteLine(method.GetCustomAttributes(false)
            .OfType<TestAttribute>().First().SomeValue);
        // prints "1"

        SomeValue = 2;

        Console.WriteLine(method.GetCustomAttributes(false)
            .OfType<TestAttribute>().First().SomeValue);
        // prints "2"

        SomeValue = 3;

        Console.WriteLine(type.GetCustomAttributes(false)
            .OfType<TestAttribute>().First().SomeValue);
        // prints "3"

        SomeValue = 4;

        Console.WriteLine(type.GetCustomAttributes(false)
            .OfType<TestAttribute>().First().SomeValue);
        // prints "4"

        Console.ReadLine();
    }
}

[AttributeUsage(AttributeTargets.All)]
class TestAttribute : Attribute
{
    public int SomeValue { get; private set; }

    public TestAttribute()
    {
        SomeValue = Program.SomeValue;
    }
}

もちろん、属性をこのように動作させるのは最善のアイデアではありません。少なくとも、GetCustomAttributesnotがこのように動作するように文書化されていることに注意してください。実際、上記のプログラムで何が起こるかは、ドキュメントでは指定されていません。

15
Roman Starkov

属性コンストラクター内にデバッガーブレークポイントを設定し、それらの属性を読み取るリフレクションコードを記述します。属性オブジェクトは、relfection APIから返されるまで作成されないことに気付くでしょう。属性はクラスごとです。それらはメタデータの一部です。

これを見てください:

Program.cs

using System;
using System.Linq;
[My(15)]
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Program started");
        var ats =
            from a in typeof(Program).GetCustomAttributes(typeof(MyAttribute), true)
            let a2 = a as MyAttribute
            where a2 != null
            select a2;

        foreach(var a in ats)
            Console.WriteLine(a.Value);

        Console.WriteLine("Program ended");
        Console.ReadLine();
    }
}

MyAttribute.cs

using System;
[AttributeUsage(validOn : AttributeTargets.Class)]
public class MyAttribute : Attribute
{
    public MyAttribute(int x)
    {
        Console.WriteLine("MyAttribute created with {0}.", x);
        Value = x;
    }

    public int Value { get; private set; }    
}

結果

Program started
MyAttribute created with 15.
15
Program ended

ただし、属性コンストラクターのパフォーマンスについては心配しないでください。それらは反射の最速の部分です:-P

6

実行可能ファイル内のメタデータまたはDLL

  • メタデータトークン呼び出すコンストラクターを示す
  • 引数

CLI実装のそのセクションに到達したら、ICustomAttributeProviderに対してGetCustomAttributes()が初めて呼び出されたときにコンストラクターを遅延呼び出しする予定です。特定の属性タイプが要求された場合、そのタイプを返すために必要なもののみを作成します。

4
Sam Harwell