57 votes

Comment obtenir le membre auquel mon attribut personnalisé a été appliqué?

Je suis de la création d'un attribut personnalisé en C# et je veux faire des choses différentes selon que l'attribut est appliqué à une méthode par rapport à une propriété. Au début, j'allais faire, new StackTrace().GetFrame(1).GetMethod() dans mon attribut personnalisé constructeur pour voir quelle méthode appelée le constructeur d'attribut, mais maintenant, je ne suis pas sûr de ce que cela va me donner. Que faire si l'attribut a été appliquée à une propriété? Serait - GetMethod() retourner un MethodBase exemple de cette propriété? Est-il une autre façon d'amener les membres à qui un attribut a été appliquée en C#?

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property,
    AllowMultiple = true)]
public class MyCustomAttribute : Attribute

Mise à jour: ok, j'ai posé la mauvaise question. De l'intérieur d'une classe attribut personnalisé, comment puis-je obtenir le membre (ou de la classe contenant le membre), à laquelle mon attribut personnalisé a été appliquée? Aaronaught suggéré à l'encontre de la marche de la pile à trouver le membre de la classe à laquelle mon attribut a été appliquée, mais sinon, comment aurais-je obtenir cette information à partir de l'intérieur du constructeur de mon attribut?

41voto

Aaronaught Points 73049

Depuis il semble y avoir beaucoup de confusion en ce qui concerne la façon dont la pile des cadres et des méthodes de travail, voici une démonstration simple:

static void Main(string[] args)
{
    MyClass c = new MyClass();
    c.Name = "MyTest";
    Console.ReadLine();
}

class MyClass
{
    private string name;

    void TestMethod()
    {
        StackTrace st = new StackTrace();
        StackFrame currentFrame = st.GetFrame(1);
        MethodBase method = currentFrame.GetMethod();
        Console.WriteLine(method.Name);
    }

    public string Name
    {
        get { return name; }
        set
        {
            TestMethod();
            name = value;
        }
    }
}

La sortie de ce programme sera:

set_Name

Propriétés en C# sont une forme de sucre syntaxique. Ils dressent vers le bas pour les getter et setter dans l'ILLINOIS, et il est possible que certains .NET langues pourraient même ne pas les reconnaître comme des propriétés de la propriété de la résolution se fait entièrement à la convention, il n'y a pas vraiment de règles dans le IL spec.

Maintenant, disons pour le moment que vous avez eu une très bonne raison pour qu'un programme voulez examiner, à sa propre pile (et il y a fort peu de raisons pratiques pour le faire). Pourquoi dans le monde voudriez-vous qu'il se comporte différemment pour les propriétés et les méthodes?

La logique derrière les attributs, c'est qu'ils sont une sorte de méta-données. Si vous souhaitez un comportement différent, le code dans l'attribut. Si un attribut peut signifier deux choses différentes selon qu'il est appliqué à une méthode ou une propriété, alors vous devriez avoir deux attributs. Définir la cible sur le premier AttributeTargets.Method et la seconde à l' AttributeTargets.Property. Simple.

Mais encore une fois, en marchant votre propre pile de ramasser quelques attributs à partir de la méthode d'appel est dangereux, au mieux. Dans un sens, vous êtes le gel de votre conception du programme, ce qui rend beaucoup plus difficile pour quelqu'un d'étendre ou de refactoriser. Ce n'est pas la manière dont les attributs sont normalement utilisés. Un de plus... approprié exemple, serait quelque chose comme une validation de l'attribut:

public class Customer
{
    [Required]
    public string Name { get; set; }
}

Ensuite, votre validateur de code, qui ne sait rien à propos de l'entité réelle qui est passée, peut faire ceci:

public void Validate(object o)
{
    Type t = o.GetType();
    foreach (PropertyInfo prop in
        t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
    {
        if (Attribute.IsDefined(prop, typeof(RequiredAttribute))
        {
            object value = prop.GetValue(o, null);
            if (value == null)
                throw new RequiredFieldException(prop.Name);
        }
    }
}

En d'autres termes, vous êtes en examinant les attributs d'une instance qui a été donnée à vous , mais que vous n'avez pas nécessairement au courant de rien sur le type de. Attributs XML, Contrat de Données d'attributs, de même Attribut les attributs de presque tous les attributs dans le .NET Framework sont utilisés de cette manière, de mettre en œuvre certaines des fonctionnalités dynamiques sur le type de l'instance , mais non à l'égard de l' état du programme ou de ce qui arrive à être sur la pile. Il est très peu probable que vous êtes réellement dans le contrôle de ce au moment où vous créez la trace de la pile.

Donc, je vais vous recommander à nouveau que vous n'avez pas utiliser la pile-la marche d'approche à moins d'avoir une très bonne raison de le faire, qui ne vous ont pas dit que nous encore. Sinon, vous êtes susceptible de trouver vous-même dans un monde de souffrance.

Si vous devez absolument (ne dites pas que nous ne vous avais pas prévenu), puis utiliser deux attributs, que l'on peut appliquer les méthodes et qui peuvent s'appliquer aux propriétés. Je pense que vous trouverez que beaucoup plus facile à travailler que d'un seul super-attribut.

41voto

Scott Dorman Points 25000

Les attributs fournissent des métadonnées et ne savent rien de la chose (classe, membre, etc.) qu'ils décorent. En revanche, la chose décorée peut demander les attributs avec lesquels elle est décorée.

Si vous devez connaître le type de la chose décorée, vous devrez explicitement le passer à votre attribut dans son constructeur.

 [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, 
    AllowMultiple = true)] 
public class MyCustomAttribute : Attribute
{
   Type type;

   public MyCustomAttribute(Type type)
   {
      this.type = type;
   }
}
 

4voto

Amirshk Points 5379

GetMethod vous renverra toujours le nom de la fonction. Si c'est une propriété, vous obtiendrez soit get_PropertyName ou set_ProperyName .

Une propriété est fondamentalement un type de méthode. Ainsi, lorsque vous implémentez une propriété, le compilateur crée deux fonctions distinctes dans les méthodes MSIL résultantes, l'une get_a et l'autre set_. C'est pourquoi, dans la trace de la pile, vous recevez ces noms.

4voto

alexdej Points 2457

attributs personnalisés sont activés par le code de l'appel de la GetCustomAttributes méthode sur la ICustomAttributeProvider de réflexion (objet) qui représente l'emplacement où l'attribut est appliqué. Ainsi, dans le cas d'une propriété, d'un peu de code serait d'obtenir la PropertyInfo de la propriété, et ensuite appeler GetCustomAttributes sur que.

Si vous voulez construire la validation de certains cadre vous devez écrire le code qui inspecte les types et les membres pour les attributs personnalisés. Par exemple, vous pouvez avoir une interface attributs mettre en œuvre pour participer à votre cadre de validation. Pourrait être aussi simple que:

public interface ICustomValidationAttribute
{
    void Attach(ICustomAttributeProvider foundOn);
}

Votre code pourrait ressembler à cette interface (par exemple) un Type:

var validators = type.GetCustomAttributes(typeof(ICustomValidationAttribute), true);
foreach (ICustomValidationAttribute validator in validators)
{
     validator.Attach(type);
}

(je présume que vous marcher toute la réflexion graphique et cela pour chaque ICustomAttributeProvider). Pour un exemple d'une approche similaire dans l'action dans la .net FX, vous pouvez regarder la WCF 'comportements' (IServiceBehavior, IOperationBehavior, etc).

Mise à jour: le .net FX ont une sorte de but générale, mais, fondamentalement, sans-papiers, l'interception dans le cadre du formulaire de ContextBoundObject et ContextAttribute. Vous pouvez rechercher sur le web quelques exemples de l'utiliser pour l'AOP.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X