Quando viene eseguito il costruttore di un attributo personalizzato?

Quando viene eseguito? Funziona per ogni object a cui lo applico o solo una volta? Può fare qualcosa, o le sue azioni sono limitate?

Quando viene eseguito il costruttore? Provalo con un campione:

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

E qual è l’output?

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

Quindi, il costruttore di attributi viene eseguito quando iniziamo ad esaminare l’attributo. Si noti che l’attributo viene recuperato dal tipo, non dall’istanza del tipo.

Il costruttore viene eseguito ogni volta che viene richiamato l’object GetCustomAttributes o ogni volta che un altro codice richiama direttamente il costruttore (non che ci sia una buona ragione per farlo, ma neanche questo è imansible).

Si noti che almeno in .NET 4.0, le istanze degli attributi non sono memorizzate nella cache ; una nuova istanza viene GetCustomAttributes ogni volta GetCustomAttributes viene chiamato 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; } } 

Non è la migliore idea che gli attributi si comportino in questo modo, ovviamente. Per lo meno, si noti che GetCustomAttributes non è documentato per comportarsi come questo; in effetti, ciò che accade nel programma di cui sopra non è specificato nella documentazione.

Imposta un break-point del debugger all’interno di un costruttore di attributi e scrivi un codice di riflessione che legge tali attributi. Noterai che gli oggetti attributo non verranno creati finché non vengono restituiti dall’API di rilocazione. Gli attributi sono per class . Fanno parte dei metadati.

Dai un’occhiata a questo:

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

Risultato

 Program started MyAttribute created with 15. 15 Program ended 

Ma non preoccuparti delle prestazioni dei costruttori di attributi. Sono la parte più veloce della riflessione 😛

I metadati negli archivi eseguibili o DLL:

  • Un token di metadati che indica al costruttore di chiamare
  • Gli argomenti

Quando arrivo a quella parte della mia implementazione CLI, pianifico di chiamare il costruttore la prima volta che viene chiamato GetCustomAttributes() per ICustomAttributeProvider . Se viene richiesto un particolare tipo di attributo, costruirò solo quelli necessari per restituire quel tipo.