web-dev-qa-db-fra.com

Quand le constructeur d'un attribut personnalisé est-il exécuté?

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?

73
devoured elysium

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.

65
Fredrik Mörk

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.

15
Roman Starkov

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:

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

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

6
Christian Klauser

Les métadonnées dans l'exécutable ou DLL stocke:

  • A jeton de métadonnées indiquant le constructeur à appeler
  • Les arguments

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.

4
Sam Harwell