Когда выполняется конструктор пользовательских атрибутов?

Когда он запускается? Выполняется ли для каждого объекта, к которому я его применяю, или только один раз? Может ли он что-либо сделать, или его действия ограничены?

Ответ 1

Когда выполняется конструктор? Попробуйте с помощью примера:

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

Итак, конструктор атрибутов запускается, когда мы начинаем изучать атрибут. Обратите внимание, что атрибут извлекается из типа, а не из экземпляра типа.

Ответ 2

Конструктор запускается каждый раз, вызываемый 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;
    }
}

Это не лучшая идея иметь атрибуты, которые ведут себя так, конечно. По крайней мере, обратите внимание, что GetCustomAttributes не документировано так себя вести; на самом деле, то, что происходит в вышеуказанной программе, не указано в документации.

Ответ 3

Задайте точку прерывания отладчика внутри конструктора атрибутов и напишите код отражения, который читает эти атрибуты. Вы заметите, что объекты атрибута не будут созданы до тех пор, пока они не будут возвращены из 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

Ответ 4

Метаданные в исполняемом файле или DLL хранятся:

  • Ток метаданных, указывающий конструктору для вызова
  • Аргументы

Когда я дойду до этого раздела моей реализации CLI, я планирую ленивый вызов конструктора при первом вызове GetCustomAttributes() для ICustomAttributeProvider. Если запрашивается конкретный тип атрибута, я создам только те, которые необходимы для возврата этого типа.