56 votes

Des Raisons convaincantes de l'Utilisation du Marqueur Interfaces Plutôt que des Attributs

Il a été discuté avant sur un Débordement de Pile que nous devrions préférer les attributs de marqueur interfaces (interfaces sans membres). La Conception de l'Interface de l'article sur MSDN affirme cette recommandation trop:

Évitez d'utiliser des marqueurs interfaces (interfaces avec les non membres).

Attributs personnalisés de fournir un moyen de marquer un type. Pour plus d'informations sur les attributs personnalisés, voir l'Écriture des Attributs Personnalisés. Les attributs personnalisés sont privilégiées lorsque vous pouvez reporter la vérification de l'attribut jusqu'à ce que le code est en cours d'exécution. Si votre scénario nécessite la compilation de la vérification, vous ne pouvez pas se conformer à la présente directive.

Il y a même une FxCop la règle à appliquer cette recommandation:

Éviter de vider les interfaces

Les Interfaces de définir les membres qui fournissent un comportement ou d'un contrat d'utilisation. La fonctionnalité décrite par l'interface peut être adopté par n'importe quel type, quel que soit l'endroit où le type s'affiche dans la hiérarchie d'héritage. Un type implémente une interface en fournissant des implémentations de l'interface de membres. Une interface vide ne définit pas de membres, et en tant que tel, ne permet pas de définir un contrat qui peut être mis en œuvre.

Si votre conception comprend vide interfaces de types sont appelés à mettre en œuvre, vous êtes probablement à l'aide d'une interface comme un marqueur, ou un moyen d'identification d'un groupe de types. Si cette identification se fera au moment de l'exécution, la bonne façon de le faire est d'utiliser un attribut personnalisé. Utilisez la présence ou l'absence de l'attribut ou de l'attribut propriétés, afin d'identifier les types de cibles. Si l'identification doit se produire au moment de la compilation, puis à l'aide d'une interface vide est acceptable.

L'article stipule qu'une seule raison de ce que vous pourriez ignorer l'avertissement: lorsque vous avez besoin de la compilation d'identification pour les types. (Ceci est cohérent avec le Design de l'Interface de l'article).

Il est sûr d'exclure un avertissement à cette règle si l'interface est utilisé pour identifier un ensemble de types à la compilation.

Voici le libellé de la question: Microsoft n'est pas conforme à leur propre recommandation dans la conception du Cadre de la Bibliothèque de la Classe (au moins dans quelques cas): IRequiresSessionState interface et IReadOnlySessionState interface. Ces interfaces sont utilisées par les ASP.NET cadre pour vérifier si il devrait permettre à l'état de session pour un gestionnaire spécifique ou non. Évidemment, il n'est pas utilisé pour la compilation d'identification de types. Pourquoi ils ne font pas ça? Je pense à deux raisons possibles:

  1. Micro-optimisation: Vérifier si un objet implémente une interface (obj is IReadOnlySessionState) est plus rapide que l'utilisation de la réflexion de vérifier pour un attribut (type.IsDefined(typeof(SessionStateAttribute), true)). La différence est négligeable, la plupart du temps, mais il pourrait vraiment de l'importance pour un chemin de code critique dans la ASP.NET moment de l'exécution. Cependant, il existe des solutions de contournement, ils ont pu s'en servir comme la mise en cache le résultat pour chaque gestionnaire de type. La chose intéressante est que les services Web ASMX (qui sont soumis aux mêmes caractéristiques de performance) en fait utiliser l' EnableSession de la propriété de l' WebMethod d'attribut à cet effet.

  2. Mise en œuvre d'interfaces sont potentiellement plus de chances d'être pris en charge de la décoration de types avec des attributs par des tiers .NET languages. Depuis ASP.NET est conçu pour être langue agnostique, et ASP.NET génère du code pour les types (éventuellement dans une troisième partie de la langue avec l'aide de CodeDom) que de mettre en œuvre lesdites interfaces basées sur l' EnableSessionState attribut de l' <%@ Page %> directive, il peut être plus judicieux d'utiliser des interfaces plutôt que des attributs.

Quelles sont les raisons convaincantes pour utiliser marqueur interfaces plutôt que des attributs?

Est-ce simplement un (prématuré?) l'optimisation ou une petite erreur dans la conception du cadre? (Pensent-ils que la réflexion est un "gros monstre aux yeux rouges"?) Pensées?

14voto

LBushkin Points 60611

J'en général d'éviter de "marqueur interfaces" parce qu'ils ne vous permettront pas de décocher un type dérivé. Mais cela mis à part, voici quelques-uns des cas particuliers que j'ai vu où marqueur interfaces serait préférable de construire-dans des méta-données à l'appui:

  1. Au moment de l'exécution de la performance des situations délicates.
  2. Compatibilité avec les langues qui ne supportent pas d'annotation ou d'attributs.
  3. Quel que soit le contexte où l'intéressé ne peut pas avoir accès aux métadonnées.
  4. Soutien pour les contraintes génériques et la variance générique (généralement de collections).

10voto

Jordão Points 29221

Pour un type générique, vous pourriez vouloir utiliser le même paramètre générique dans une interface marqueur. Cela n'est pas réalisable par un attribut:

interface MyInterface<T> {}

class MyClass<T, U> : MyInterface<U> {}

class OtherClass<T, U> : MyInterface<IDictionary<U, T>> {}

Ce type d'interface peut être utile d'associer un type à un autre.

Une autre bonne utilisation d'un marqueur de l'interface, c'est quand vous voulez pour créer une sorte de mixin:

interface MyMixin {}

static class MyMixinMethods {
  public static void Method(this MyMixin self) {}
}

class MyClass : MyMixin {
}

Le acyclique modèle visiteur utilise aussi. Le terme de "dégénéré" interface " est parfois utilisé.

Mise à JOUR:

Je ne sais pas si cela compte, mais je les ai utilisés pour marquer des classes pour un post-compilateur pour travailler sur.

6voto

Mark Seemann Points 102767

Microsoft n'a pas strictement les lignes directrices lorsqu'ils ont fait .NET 1.0, parce que les lignes directrices évolué de pair avec le cadre, et certaines des règles qu'ils n'ont pas à apprendre jusqu'à ce qu'il était trop tard pour changer l'API.

IIRC, les exemples que vous mentionnez appartiennent à la BCL 1.0, ainsi que l'explique.

Ceci est expliqué dans le Cadre des lignes Directrices de Conception.


Cela dit, le livre est aussi, remarque que "[A]ttribute test est beaucoup plus coûteux que d'un type de vérification" (dans une barre latérale par Rico Mariani).

Il va de soi que, parfois, vous avez besoin de l'interface marqueur pour le moment de la compilation, la vérification, ce qui n'est pas possible avec un attribut. Cependant, je trouve l'exemple donné dans le livre (p. 88) peu convaincante, je ne vais pas le répéter ici.

4voto

herzmeister Points 7077

Je suis fortement pro Marqueur Interfaces. Je n'ai jamais aimé les Attributs. Je les vois comme une sorte de méta-informations pour les classes et les membres prévu par exemple pour les débogueurs à regarder. Semblables à des Exceptions, ils ne devraient pas influer sur le traitement normal de la logique, à mon humble avis.

3voto

mikeng Points 906

À partir d'un point de vue du code, je pense que je préfère le Marqueur de l'Interface syntaxe en raison de la construction dans les mots clés as et is. Attribut de marquage nécessite un peu plus de code.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[MarkedByAttribute]
public class MarkedClass : IMarkByInterface
{
}

public class MarkedByAttributeAttribute : Attribute
{
}

public interface IMarkByInterface
{
}

public static class AttributeExtension
{
    public static bool HasAttibute(this object obj, Type attributeType)
    {
        var hasAttribute = Attribute.GetCustomAttribute(obj.GetType(), attributeType);
        return hasAttribute != null;
    }
}

[TestClass]
public class ClassMarkingTests
{
    private MarkedClass _markedClass;

    [TestInitialize]
    public void Init()
    {
        _markedClass = new MarkedClass();
    }

    [TestMethod]
    public void TestClassAttributeMarking()
    {
        var hasMarkerAttribute = _markedClass.HasAttibute(typeof(MarkedByAttributeAttribute));
        Assert.IsTrue(hasMarkerAttribute);
    }

    [TestMethod]
    public void TestClassInterfaceMarking()
    {
        var hasMarkerInterface = _markedClass as IMarkByInterface;
        Assert.IsTrue(hasMarkerInterface != null);            
    }
} 

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