Quand est-il exécuté ? S'exécute-t-il pour chaque objet sur lequel je l'applique, ou une seule fois ? Peut-il faire quelque chose, ou ses actions sont-elles limitées ?
Réponses
Trop de publicités?Quand le constructeur est-il exécuté ? Essayez-le avec un exemple :
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 quel est le résultat ?
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, et non de l'instance du type.
Le constructeur est exécuté à chaque fois le site GetCustomAttributes
est invoqué ou lorsqu'un autre code invoque directement le constructeur. .
Notez qu'au moins dans .NET 4.0, les instances de l'attribut sont pas en cachette ; une nouvelle instance est construite à chaque fois GetCustomAttributes
s'appelle :
[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 entendu, ce n'est pas la meilleure idée de faire en sorte que les attributs se comportent de cette manière. À tout le moins, notez que GetCustomAttributes
est no n'est pas documenté pour se comporter de cette manière ; 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 à l'intérieur d'un constructeur d'attributs et écrivez du code de réflexion qui lit ces attributs. Vous remarquerez que les objets attributs ne seront pas créés avant d'être renvoyés par l'API de réflexion. Les attributs sont par classe . Ils font partie des métadonnées.
Jetez un coup d'oeil à ça :
Programme.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();
}
}
MonAttribut.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; }
}
Résultat
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 de l'exécutable ou de la DLL sont stockées :
- A jeton de métadonnées indiquant le constructeur à appeler
- Les arguments
Lorsque j'arriverai à cette section de mon implémentation CLI, je prévois d'appeler le constructeur paresseusement la première fois. GetCustomAttributes()
est appelé pour le ICustomAttributeProvider
. Si un type d'attribut particulier est demandé, je ne construirai que ceux qui sont nécessaires pour retourner ce type.