Come identificare a livello di codice il numero di riferimenti a un metodo con C #

StackOverflow https://stackoverflow.com/questions/106329

  •  01-07-2019
  •  | 
  •  

Domanda

Di recente ho ereditato l'applicazione console C # che ha bisogno di potatura e pulizia. Per farla breve, l'app è costituita da un'unica classe contenente oltre 110.000 righe di codice. Sì, oltre 110.000 linee in una sola classe. E, naturalmente, l'app è fondamentale per la nostra attività, in esecuzione 24 ore su 24, aggiornando i dati utilizzati su un sito Web dinamico. Sebbene mi sia stato detto che il mio predecessore era "davvero un buon programmatore", è ovvio che non era affatto interessato a OOP (o controllo della versione).

Comunque ... mentre mi sono familiarizzato con il codice ho trovato molti metodi dichiarati, ma a cui non si è mai fatto riferimento. Sembra che per copiare la versione del codice sia stato usato copia / incolla, ad esempio dire che ho un metodo chiamato getSomethingImportant (), è probabile che esista un altro metodo chiamato getSomethingImortant_July2007 () (il modello è functionName_ [datestamp] nella maggior parte dei casi). Sembra che quando al programmatore è stato chiesto di apportare una modifica a getSomethingImportant () avrebbe copiato / incollato, quindi rinominato in getSomethingImortant_Date, apportato modifiche a getSomethingImortant_Date, quindi modificato tutte le chiamate di metodo nel codice con il nuovo metodo, lasciando il codice ma mai referenziato.

Vorrei scrivere una semplice app console che esegue la scansione dell'unica classe enorme e restituisce un elenco di tutti i metodi con il numero di volte in cui è stato fatto riferimento a ciascun metodo. Secondo le mie stime ci sono oltre 1000 metodi, quindi farlo a mano richiederebbe un po 'di tempo.

Esistono classi all'interno del framework .NET che posso usare per esaminare questo codice? O qualsiasi altro strumento utile che possa aiutare a identificare metodi dichiarati ma a cui non si fa mai riferimento?

(Domanda a margine: qualcun altro ha mai visto un'app C # come questa, una vera e propria grande classe? È più o meno un enorme processo procedurale, so che questo è il primo che ho visto, almeno di queste dimensioni.)

È stato utile?

Soluzione

Potresti provare a utilizzare NDepend se hai solo bisogno di estrarre alcune statistiche sulla tua classe. Si noti che questo strumento si affida a Mono.Cecil internamente per ispezionare gli assiemi.

Altri suggerimenti

Scarica la prova gratuita di Resharper. Utilizza Resharper- > Cerca- > Trova gli usi nel file (Ctrl-Shift-F7) per evidenziare tutti gli usi. Inoltre, nella barra di stato verrà visualizzato un conteggio. Se vuoi cercare tra più file, puoi farlo anche usando Ctrl-Alt-F7.

Se non ti piace, cerca nel testo il nome della funzione in Visual Studio (Ctrl-Shift-F), questo dovrebbe dirti quante occorrenze sono state trovate nella soluzione e dove si trovano.

Per completare la risposta Romain Verdier , approfondiamo un po 'ciò che NDepend può offrirti qui. ( Dichiarazione di non responsabilità: sono uno sviluppatore del team NDepend )

NDepend consente di eseguire query sul codice .NET con alcune query LINQ. Sapere quali metodi chiamano e viene chiamato da quali altri, è semplice come scrivere la seguente query LINQ:

from m in Application.Methods
select new { m, m.MethodsCalled, m.MethodsCallingMe }

Il risultato di questa query è presentato in un modo che semplifica la navigazione di chiamanti e chiamate (e il suo 100% integrato in Visual Studio).

Metodi NDipendenti chiamanti e chiamate


Esistono molte altre funzionalità di NDepend che possono aiutarti. Ad esempio puoi fare clic con il pulsante destro del mouse su un metodo in Visual Studio > NDepend > Seleziona i metodi ... > che mi stanno usando (direttamente o indirettamente) ...

Fai clic con il tasto destro del mouse sul metodo NDepend

Viene generata la seguente query di codice ...

from m in Methods 
let depth0 = m.DepthOfIsUsing("NUnit.Framework.Constraints.ConstraintExpression.Property(String)")
where depth0  >= 0 orderby depth0
select new { m, depth0 }

... che corrisponde ai chiamanti diretti e indiretti, con la profondità delle chiamate (1 significa chiamante diretto, 2 significa chiamante di chiamanti diretti e così via).

Chiamate con metodo indiretto NDepend

E quindi facendo clic sul pulsante Esporta in grafico , si ottiene un grafico di chiamata del metodo pivot (ovviamente potrebbe essere il contrario, ovvero il metodo chiamato direttamente o indirettamente da un particolare pivot metodo).

Grafico chiamate NDepend

Non credo che tu voglia scriverlo tu stesso: acquista NDepend e usa Lingua query codice

FXCop ha una regola che identificherà i metodi privati ??non utilizzati. Quindi puoi contrassegnare tutti i metodi come privati ??e far sì che generi un elenco.

FXCop ha anche una lingua se vuoi diventare più fantasioso http://www.binarycoder.net/fxcop/

Se non vuoi sborsare per NDepend, dal momento che sembra che ci sia una sola classe in un singolo assembly - commenta i metodi e compila. Se viene compilato, eliminali: non avrai problemi di ereditarietà, metodi virtuali o simili. So che sembra primitivo, ma a volte il refactoring è solo un lavoro grugnito come questo. Questo è un po 'supponendo che tu abbia dei test unitari che esegui dopo ogni build fino a quando non hai ripulito il codice (rosso / verde / refactor).

La finestra dell'analizzatore in Reflector può mostrare dove viene chiamato un metodo ( Utilizzato da).
Sembra che ci vorrebbe molto tempo per ottenere le informazioni in quel modo però.
Potresti guardare l'API fornita da Reflector per scrivere componenti aggiuntivi e vedere se riesci a ottenere il lavoro grugnito dell'analisi in quel modo. Mi aspetto che il codice sorgente per il componente aggiuntivo metriche di codice potrebbe dirti un po 'su come ottenere informazioni sui metodi dall'API reflector.

Modifica: anche il visualizzatore modello codice il componente aggiuntivo per Reflector potrebbe essere di aiuto. È un buon modo per esplorare l'API Reflector.

Non so nulla di ciò che è stato creato per gestire questo caso specifico, ma potresti usare Mono.Cecil. Riflettere gli assiemi e quindi contare i riferimenti nell'IL. Non dovrebbe essere troppo duro.

Non esiste uno strumento semplice per farlo nello stesso .NET framework. Tuttavia, non penso che tu abbia davvero bisogno di un elenco di metodi inutilizzati contemporaneamente. A mio modo di vedere, passerai attraverso il codice e per ogni metodo controllerai se non è in uso e poi lo eliminerai. Vorrei utilizzare Visual Studio "Trova riferimenti" comando per farlo. In alternativa puoi utilizzare Resharper con il suo " Analize " finestra. Oppure puoi semplicemente usare lo strumento di analisi del codice di Visual Studio per trovare tutti i metodi privati ??non utilizzati.

Prova a fare in modo che il compilatore emetta file assembler, come nelle istruzioni x86, non in assembly .NET.

Perché? Perché è molto più semplice analizzare il codice assembler che non il codice C # o gli assembly .NET.

Ad esempio, una dichiarazione di funzione / metodo è simile a questa:

    .string "w+"
    .text
    .type   create_secure_tmpfile, @function
create_secure_tmpfile:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $-1, -8(%ebp)
    subl    $4, %esp

e i riferimenti a funzioni / metodi saranno simili a questo:

    subl    $12, %esp
    pushl   24(%ebp)
    call    create_secure_tmpfile
    addl    $16, %esp
    movl    20(%ebp), %edx
    movl    %eax, (%edx)

Quando vedi " create_secure_tmpfile: " sai di avere una dichiarazione funzione / metodo e quando vedi " chiama create_secure_tmpfile " sai di avere un riferimento a funzione / metodo. Questo può essere abbastanza buono per i tuoi scopi, ma in caso contrario sono necessari solo alcuni passaggi prima di poter generare un albero delle chiamate molto carino per l'intera applicazione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top