¿Cuándo se ejecuta el constructor de un atributo personalizado?

¿Cuándo se ejecuta? ¿Funciona para cada objeto al que lo aplico, o solo una vez? ¿Puede hacer algo o sus acciones están restringidas?

¿Cuándo se ejecuta el constructor? Pruébelo con una muestra:

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; } } 

Y cual es la salida?

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

Entonces, el constructor del atributo se ejecuta cuando comenzamos a examinar el atributo. Tenga en cuenta que el atributo se obtiene del tipo, no la instancia del tipo.

El constructor se ejecuta cada vez que se invoca GetCustomAttributes , o cuando algún otro código invoca el constructor directamente (no es que haya una buena razón para hacerlo, pero tampoco es imposible).

Tenga en cuenta que, al menos en .NET 4.0, las instancias de atributo no se almacenan en caché ; se crea una nueva instancia cada vez que se llama 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().First().SomeValue); // prints "1" SomeValue = 2; Console.WriteLine(method.GetCustomAttributes(false) .OfType().First().SomeValue); // prints "2" SomeValue = 3; Console.WriteLine(type.GetCustomAttributes(false) .OfType().First().SomeValue); // prints "3" SomeValue = 4; Console.WriteLine(type.GetCustomAttributes(false) .OfType().First().SomeValue); // prints "4" Console.ReadLine(); } } [AttributeUsage(AttributeTargets.All)] class TestAttribute : Attribute { public int SomeValue { get; private set; } public TestAttribute() { SomeValue = Program.SomeValue; } } 

No es la mejor idea que los atributos se comporten así, por supuesto. Como mínimo, tenga en cuenta que GetCustomAttributes no está documentado para comportarse así; de hecho, lo que sucede en el progtwig anterior no está especificado en la documentación.

Establezca un punto de interrupción del depurador dentro de un constructor de atributos y escriba un código de reflexión que lea esos atributos. Notará que los objetos de atributo no se crearán hasta que se devuelvan desde la API de relieves. Los atributos son por clase . Ellos son parte de los metadatos.

Echa un vistazo a esto:

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; } } 

Resultado

 Program started MyAttribute created with 15. 15 Program ended 

Pero no se preocupe por el rendimiento de los constructores de atributos. Son la parte más rápida de la reflexión 😛

Los metadatos en el archivo ejecutable o DLL almacenan:

  • Un token de metadatos que indica que el constructor debe llamar
  • Los argumentos

Cuando llegue a esa sección de la implementación de mi CLI, planeo llamar al constructor la primera vez que se llame a GetCustomAttributes() para ICustomAttributeProvider . Si se solicita un tipo de atributo en particular, solo construiré los necesarios para devolver ese tipo.