Quand est-il exécuté? Exécute-t-il pour chaque objet auquel je l'applique, ou une seule fois? Peut-il faire quelque chose ou ses actions sont limitées?
Quand le constructeur est-il exécuté? Essayez-le avec un échantillon:
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; }
}
Et quelle est la sortie?
Creating MyClass instance
Setting value in MyClass instance
Getting attributes for MyClass type
Running constructor
Ainsi, le constructeur d'attribut est exécuté lorsque nous commençons à examiner l'attribut. Notez que l'attribut est récupéré à partir du type, pas l'instance du type.
Le constructeur est exécuté à chaque fois le GetCustomAttributes
est appelé, ou chaque fois qu'un autre code appelle directement le constructeur (pas qu'il y ait une bonne raison de le faire, mais ce n'est pas impossible non plus).
Notez qu'au moins dans .NET 4.0, les instances d'attribut sont non mises en cache; une nouvelle instance est construite à chaque appel de 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;
}
}
Bien sûr, ce n'est pas la meilleure idée d'avoir des attributs comme ça. À tout le moins, notez que GetCustomAttributes
est pas documenté pour se comporter comme ceci; en fait, ce qui se passe dans le programme ci-dessus n'est pas spécifié dans la documentation.
Définissez un point d'arrêt du débogueur dans un constructeur d'attributs et écrivez du code de réflexion qui lit ces attributs. Vous remarquerez que les objets d'attribut ne seront pas créés tant qu'ils ne seront pas renvoyés par l'API de relection. Les attributs sont par classe. Ils font partie des métadonnées.
Jetez un œil à ceci:
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();
}
}
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
Mais ne vous inquiétez pas des performances des constructeurs d'attributs. Ils sont la partie la plus rapide de la réflexion :-P
Les métadonnées dans l'exécutable ou DLL stocke:
Lorsque j'arrive à cette section de mon implémentation CLI, je prévois d'appeler par la suite le constructeur la première fois que GetCustomAttributes()
est appelée pour ICustomAttributeProvider
. Si un type d'attribut particulier est demandé, je ne construirai que ceux requis pour renvoyer ce type.