web-dev-qa-db-fra.com

Meilleure pratique pour marquer une méthode appelée par réflexion?

Notre logiciel a plusieurs classes qui devraient être trouvées dynamiquement via la réflexion. Les classes ont toutes un constructeur avec une signature spécifique via laquelle le code de réflexion instancie les objets.
Cependant, lorsque quelqu'un vérifie si la méthode est référencée (par exemple via Visual studio Code Lens), la référence via la réflexion n'est pas comptée. Les gens peuvent manquer leurs références et supprimer (ou modifier) ​​des méthodes apparemment inutilisées.

Comment marquer/documenter les méthodes destinées à être appelées par réflexion?

Idéalement, la méthode doit être marquée de telle manière que les collègues et Visual Studio/Roslyn et d'autres outils automatisés "voient" que la méthode est destinée à être appelée via la réflexion.

Je connais deux options que nous pouvons utiliser, mais les deux ne sont pas tout à fait satisfaisantes. Étant donné que Visual Studio ne peut pas trouver les références:

  • Utilisez un Attribut personnalisé et marquez le constructeur avec cet attribut.
    • Un problème est que les propriétés d'attribut ne peuvent pas être une référence de méthode, par conséquent le constructeur affichera toujours comme ayant 0 références.
    • Les collègues qui ne connaissent pas l'attribut personnalisé l'ignoreront probablement.
    • Un avantage de mon approche actuelle est que la partie réflexion peut utiliser l'attribut pour trouver le constructeur qu'elle doit appeler.
  • Utilisez des commentaires pour documenter qu'une méthode/un constructeur doit être appelé par réflexion.
    • Les outils automatisés ignorent les commentaires (et les collègues peuvent également le faire).
    • Commentaires sur la documentation Xml peut être utilisé pour que Visual Studio compte une référence supplémentaire à la méthode/constructeur:
      Soit MyPlugin la classe dont le constructeur à invoquer par réflexion. Supposons que le code de réflexion appelant recherche les constructeurs qui prennent un paramètre int. La documentation suivante fait que la lentille de code montre le constructeur ayant 1 référence:
      /// <see cref="MyPlugin.MyPlugin(int)"/> is invoked via reflection

Quelles meilleures options existent?
Quelle est la meilleure pratique pour marquer une méthode/un constructeur qui doit être appelé par réflexion?

11

Une combinaison des solutions suggérées:

  • Utilisez des balises de documentation XML pour documenter que le constructeur/la méthode est appelée via la réflexion.
    Cela devrait clarifier l'utilisation prévue à mes collègues (et à moi-même).
  • Utilisez le "truc" via le <see> - balise pour augmenter le nombre de références pour le constructeur/méthode.
    Cela rend cette lentille de code et les références de référence montrent que le constructeur/méthode est référencé.
  • Annoter avec Resharper's UsedImplicitlyAttribute
    • Resharper est une norme de facto et [UsedImplicitly] a précisément la sémantique voulue.
    • Ceux qui n'utilisent pas Resharper peuvent installer les Annotations JetBrains ReSharper via NuGet:
      PM> Install-Package JetBrains.Annotations.
  • S'il s'agit d'une méthode privée et que vous utilisez l'analyse de code de Visual Studio, utilisez SupressMessageAttribute pour le message CA1811: Avoid uncalled private code .

Par exemple:

class MyPlugin
{
    /// <remarks>
    /// <see cref="MyPlugin.MyPlugin(int)"/> is called via reflection.
    /// </remarks>
    [JetBrains.Annotations.UsedImplicitly]
    public MyPlugin(int arg)
    {
        throw new NotImplementedException();
    }

    /// <remarks>
    /// <see cref="MyPlugin.MyPlugin(string)"/> is called via reflection.
    /// </remarks>
    [JetBrains.Annotations.UsedImplicitly]
    [System.Diagnostics.CodeAnalysis.SuppressMessage(
        "Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode",
        Justification = "Constructor is called via reflection")]
    private MyPlugin(string arg)
    {
        throw new NotImplementedException();
    }
}

La solution confère l'utilisation prévue du constructeur aux lecteurs humains et aux 3 systèmes d'analyse de code statique les plus utilisés avec C # et Visual Studio.
L'inconvénient est qu'un commentaire et une ou deux annotations peuvent sembler un peu redondants.

12

Je n'ai jamais eu ce problème dans un projet .Net, mais j'ai régulièrement le même problème avec les projets Java. Mon approche habituelle consiste à utiliser l'annotation @SuppressWarnings("unused") en ajoutant un commentaire expliquant pourquoi (documenter la raison de la désactivation des avertissements fait partie de mon style de code standard - chaque fois que le compilateur ne parvient pas à comprendre quelque chose, je suppose qu'il est probable qu'un humain puisse également lutter). Cela a l'avantage de garantir automatiquement les outils d'analyse statique sont conscients que le code n'est pas censé avoir des références directes et donnent une raison détaillée aux lecteurs humains.

L'équivalent C # de Java's @SuppressWarnings est SuppressMessageAttribute . Pour les méthodes privées , vous pouvez utiliser le message CA1811: éviter le code privé non appelé ; par exemple.:

class MyPlugin
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage(
        "Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode",
        Justification = "Constructor is called via reflection")]
    private MyPlugin(int arg)
    {
        throw new NotImplementedException();
    }
}
5
Jules

Une alternative à la documentation serait d'avoir des tests unitaires pour s'assurer que les appels de réflexion s'exécutent correctement.

De cette façon, si quelqu'un modifie ou supprime les méthodes, votre processus de génération/test devrait vous alerter que vous avez cassé quelque chose.

2
Nick Williams

Eh bien, sans voir votre code, cela ressemble à ceci serait un bon endroit pour introduire un héritage. Peut-être une méthode virtuelle ou abstraite que le constructeur de ces classes peut appeler? Si vous êtes la méthode que vous essayez de marquer est juste le constructeur, alors vous essayez vraiment de marquer une classe et non une méthode ouais? Quelque chose que j'ai fait dans le passé pour marquer les classes est de créer une interface vide. Ensuite, les outils d'inspection de code et la refactorisation peuvent rechercher des classes qui implémentent l'interface.

0
Jeff Sall