it-swarm.com.de

Best Practice zum Markieren einer Methode, die über Reflexion aufgerufen wird?

Unsere Software verfügt über mehrere Klassen, die dynamisch über Reflexion gefunden werden sollten. Die Klassen haben alle einen Konstruktor mit einer bestimmten Signatur, über die der Reflektionscode Objekte instanziiert.
Wenn jedoch jemand prüft, ob auf die Methode verwiesen wird (z. B. über Visual Studio Code Lens), wird die Referenz über Reflexion nicht gezählt. Menschen können ihre Referenzen verpassen und scheinbar nicht verwendete Methoden entfernen (oder ändern).

Wie sollen Methoden markiert/dokumentiert werden, die durch Reflexion aufgerufen werden sollen?

Idealerweise sollte die Methode so gekennzeichnet sein, dass sowohl Kollegen als auch Visual Studio/Roslyn und andere automatisierte Tools erkennen, dass die Methode über Reflektion aufgerufen werden soll.

Ich kenne zwei Optionen, die wir verwenden können, aber beide sind nicht ganz zufriedenstellend. Da Visual Studio die Referenzen nicht finden kann:

  • Verwenden Sie ein benutzerdefiniertes Attribut und markieren Sie den Konstruktor mit diesem Attribut.
    • Ein Problem besteht darin, dass Attributeigenschaften keine Methodenreferenz sein können. Daher zeigt der Konstruktor weiterhin 0 Referenzen an.
    • Kollegen, die mit dem benutzerdefinierten Attribut nicht vertraut sind, werden es wahrscheinlich ignorieren.
    • Ein Vorteil meines aktuellen Ansatzes ist, dass der Reflexionsteil das Attribut verwenden kann, um den Konstruktor zu finden, den er aufrufen soll.
  • Verwenden Sie Kommentare, um zu dokumentieren, dass eine Methode/ein Konstruktor über Reflektion aufgerufen werden soll.
    • Automatisierte Tools ignorieren Kommentare (und Kollegen tun dies möglicherweise auch).
    • Xml Documentation Comments kann verwendet werden, damit Visual Studio einen zusätzlichen Verweis auf die Methode/den Konstruktor zählt:
      Sei MyPlugin die Klasse, deren Konstruktor durch Reflexion aufgerufen werden soll. Angenommen, der aufrufende Reflection-Code sucht nach Konstruktoren, die einen int -Parameter verwenden. In der folgenden Dokumentation wird gezeigt, dass die Codelinse den Konstruktor mit 1 Referenz zeigt:
      /// <see cref="MyPlugin.MyPlugin(int)"/> is invoked via reflection

Welche besseren Möglichkeiten gibt es?
Was ist die beste Vorgehensweise zum Markieren einer Methode/eines Konstruktors, die bzw. der über Reflexion aufgerufen werden soll?

11

Eine Kombination der vorgeschlagenen Lösungen:

  • Verwenden Sie XML-Dokumentations-Tags, um zu dokumentieren, dass der Konstruktor/die Methode über Reflection aufgerufen wird.
    Dies sollte den Kollegen (und meinem zukünftigen Selbst) die beabsichtigte Verwendung klarstellen.
  • Verwenden Sie den 'Trick' über das <see> - Tag zum Erhöhen der Referenzanzahl für den Konstruktor/die Methode.
    Dadurch wird die Codelinse und die Suchreferenzen angezeigt, auf die der Konstruktor/die Methode verwiesen wird.
  • Kommentieren Sie mit Resharper's UsedImplicitlyAttribute
    • Resharper ist ein De-facto-Standard und [UsedImplicitly] hat genau die beabsichtigte Semantik.
    • Wenn Sie Resharper nicht verwenden, können Sie JetBrains ReSharper Annotations über NuGet installieren:
      PM> Install-Package JetBrains.Annotations.
  • Wenn es sich um eine private Methode handelt und Sie die Codeanalyse von Visual Studio verwenden, verwenden Sie SupressMessageAttribute für die Nachricht CA1811: Avoid uncalled private code .

Zum Beispiel:

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

Die Lösung überträgt die beabsichtigte Verwendung des Konstruktors sowohl an menschliche Leser als auch an die drei statischen Code-Analysesysteme, die am häufigsten mit C # und Visual Studio verwendet werden.
Der Nachteil ist, dass sowohl ein Kommentar als auch ein oder zwei Anmerkungen etwas überflüssig erscheinen.

12

Ich hatte dieses Problem noch nie in einem .Net-Projekt, aber ich habe regelmäßig das gleiche Problem mit Java -Projekten. Mein üblicher Ansatz besteht darin, die Annotation @SuppressWarnings("unused") hinzuzufügen Ein Kommentar, der erklärt, warum (die Dokumentation des Grundes für das Deaktivieren von Warnungen gehört zu meinem Standard-Codestil - jedes Mal, wenn der Compiler etwas nicht herausfinden kann, gehe ich davon aus, dass wahrscheinlich auch ein Mensch Probleme hat). Dies hat den Vorteil, dass dies automatisch sichergestellt wird Statische Analysewerkzeuge sind sich bewusst, dass der Code keine direkten Verweise enthalten soll, und geben einen detaillierten Grund für menschliche Leser an.

Das C # -Äquivalent von Javas @SuppressWarnings ist SuppressMessageAttribute . Für private Methoden können Sie die Nachricht verwenden CA1811: Vermeiden Sie nicht aufgerufenen privaten Code ; z.B.:

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

Eine Alternative zur Dokumentation wäre, Unit-Tests durchzuführen, um sicherzustellen, dass die Reflection-Aufrufe erfolgreich ausgeführt werden.

Auf diese Weise sollte Ihr Build-/Testprozess Sie benachrichtigen, wenn jemand die Methoden ändert oder entfernt, dass Sie etwas kaputt gemacht haben.

2
Nick Williams

Nun, ohne Ihren Code zu sehen, klingt es so, als wäre dies ein guter Ort, um eine Vererbung einzuführen. Vielleicht eine virtuelle oder abstrakte Methode, die der Konstruktor dieser Klassen aufrufen kann? Wenn Sie die Methode sind, die Sie markieren möchten, ist dies nur der Konstruktor, dann versuchen Sie wirklich, eine Klasse und keine Methode zu markieren, ja? In der Vergangenheit habe ich zum Markieren von Klassen eine leere Schnittstelle erstellt. Dann können Code-Inspektions-Tools und Refactoring nach Klassen suchen, die die Schnittstelle implementieren.

0
Jeff Sall