Le migliori pratiche per contrassegnare un metodo che viene chiamato tramite riflessione?

8

Il nostro software ha diverse classi che dovrebbero essere trovate dinamicamente tramite la riflessione. Le classi hanno tutte un costruttore con una firma specifica tramite la quale il codice di riflessione crea un'istanza degli oggetti.
Tuttavia, quando qualcuno controlla se il metodo è referenziato (ad esempio tramite Visual Studio Code Lens), il riferimento tramite riflessione non viene conteggiato. Le persone possono perdere i loro riferimenti e rimuovere (o modificare) i metodi apparentemente inutilizzati.

Come dovremmo marcare / documentare i metodi che si intendono chiamare tramite riflessione?

Idealmente, il metodo dovrebbe essere contrassegnato in modo tale che sia i colleghi che Visual Studio / Roslyn e gli altri strumenti automatici "vedano" che il metodo debba essere chiamato tramite riflessione.

Conosco due opzioni che possiamo usare, ma entrambe non sono abbastanza soddisfacenti. Poiché Visual Studio non riesce a trovare i riferimenti:

  • Utilizza una personalizzata) Attributo e contrassegna il costruttore con questo attributo.
    • Un problema è che le proprietà degli attributi non possono essere un riferimento al metodo, quindi il costruttore mostrerà ancora come 0 riferimenti.
    • I colleghi che non hanno familiarità con l'attributo personalizzato probabilmente ignoreranno esso.
    • Un vantaggio del mio attuale approccio è la parte di riflessione che può essere utilizzata l'attributo per trovare il costruttore che dovrebbe chiamare.
  • Utilizza i commenti per documentare che un metodo / costruttore è destinato a essere chiamato tramite riflessione.
    • Gli strumenti automatici ignorano i commenti (e anche i colleghi potrebbero farlo).
    • Commenti della documentazione Xml possono essere utilizzati contare su Visual Studio un ulteriore riferimento al metodo / costruttore:
      Lascia che MyPlugin sia la classe il cui costruttore invoca tramite reflection. Supponiamo che il richiamo del codice di riflessione cerchi i costruttori che prendono un parametro int . La seguente documentazione rende questo codice obiettivo mostra al costruttore 1 riferimento:
      /// <see cref="MyPlugin.MyPlugin(int)"/> is invoked via reflection

Quali opzioni migliori esistono?
Qual è la migliore pratica per contrassegnare un metodo / costruttore che si intende chiamare tramite riflessione?

    
posta Kasper van den Berg 24.04.2017 - 15:48
fonte

4 risposte

9

Una combinazione delle soluzioni suggerite:

  • Utilizzare i tag di documentazione XML per documentare che il costruttore / metodo è chiamato tramite riflessione.
    Questo dovrebbe chiarire l'uso previsto per i colleghi (e il mio io futuro).
  • Utilizza il "trucco" tramite <see> -tag per aumentare il conteggio dei riferimenti per il costruttore / metodo.
    Questo fa sì che l'obiettivo del codice e i riferimenti di ricerca mostrino che il costruttore / metodo è referenziato.
  • Annota con UsedImplicitlyAttribute di Resharper's
    • Il resharper è uno standard de facto e [UsedImplicitly] ha esattamente la semantica voluta.
    • Chi non utilizza Resharper può installare JetBrains ReSharper Annotazioni via NuGet:
      PM > Install-Package JetBrains.Annotations .
  • Se si tratta di un metodo privato e si sta utilizzando l'analisi del codice di Visual Studio, usa SupressMessageAttribute per il messaggio CA1811: Avoid uncalled private code .

Ad esempio:

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 soluzione conferisce l'uso previsto del costruttore sia ai lettori umani sia ai 3 sistemi di analisi del codice statici più utilizzati con C # e Visual Studio.
Lo svantaggio è che sia un commento sia una o due annotazioni potrebbero sembrare un po 'ridondanti.

    
risposta data 25.04.2017 - 09:56
fonte
4

Non ho mai avuto questo problema in un progetto .Net, ma regolarmente ho lo stesso problema con i progetti Java. Il mio approccio abituale consiste nell'usare l'annotazione @SuppressWarnings("unused") aggiungendo un commento che spieghi perché (documentare il motivo della disabilitazione di eventuali avvisi fa parte del mio stile di codice standard - ogni volta che il compilatore non riesce a capire qualcosa, presumo sia probabile che un anche l'umano potrebbe lottare). Ciò ha il vantaggio di garantire automaticamente che gli strumenti di analisi statici siano consapevoli del fatto che il codice non dovrebbe avere riferimenti diretti e fornire una motivazione dettagliata per i lettori umani.

L'equivalente C # di @SuppressWarnings di Java è SuppressMessageAttribute . Per i metodi private è possibile utilizzare il messaggio CA1811: evitare codice privato non richiamato ; per esempio:.

class MyPlugin
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage(
        "Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode",
        Justification = "Constructor is called via reflection")]
    private MyPlugin(int arg)
    {
        throw new NotImplementedException();
    }
}
    
risposta data 24.04.2017 - 16:38
fonte
2

Un'alternativa alla documentazione sarebbe avere un test unitario che assicuri che le chiamate di riflessione siano eseguite con successo.

In questo modo se qualcuno cambia o rimuove i metodi il processo di build / test dovrebbe avvisarti che hai rotto qualcosa.

    
risposta data 27.04.2017 - 00:01
fonte
0

Beh, senza vedere il tuo codice, è come se questo fosse un buon posto per introdurre dell'eredità. Forse un metodo virtuale o astratto che il costruttore di queste classi può chiamare? Se sei il metodo che stai cercando di contrassegnare è solo il costruttore, allora stai davvero cercando di contrassegnare una classe e non un metodo sì? Qualcosa che ho fatto in passato per contrassegnare le classi è creare un'interfaccia vuota. Quindi gli strumenti di ispezione del codice e il refactoring possono cercare classi che implementano l'interfaccia.

    
risposta data 24.04.2017 - 19:57
fonte

Leggi altre domande sui tag