8

Attualmente se si desidera verificare i riferimenti circolari all'interno di una soluzione, selezionare Architecture - Generate Dependency Graph - For Solution. Quindi dalla nuova scheda che si apre seleziono Layout - Analyzers - Circular References Analyzer. Infine, se eseguo il drill down dei singoli assiemi e ci sono riferimenti circolari, posso vederli evidenziati in rosso sul grafico e vengono visualizzati anche come avvisi nell'elenco degli errori.Visual Studio 2012 - Trova riferimenti circolari in modo efficiente

Dal momento che ho intenzione di individuare riferimenti circolari anche tra metodi della stessa classe, questo è abbastanza soggetto a errori e richiede tempo su un codebase moderatamente grande.

Mi piacerebbe sapere se c'è un modo per ottenere tutti gli avvertimenti in una volta senza dover espandere i nodi o forse attivare l'evidenziazione per i nodi padre in modo da poter eseguire il drill down solo su assi che sicuramente contengono circolari Riferimenti.

NDepend dovrebbe essere in grado di aiutare ma preferisco mantenere le cose il più semplice possibile, quindi sono sempre cauto nell'adottare strumenti aggiuntivi.

+0

Utilità correlata per trovare rapidamente riferimenti circolari: http://stackoverflow.com/a/43374622/64334 –

risposta

6

Sì NDepend può trovare riferimenti circolari in modo efficiente Mi spiego come perché potrebbe essere più facile di quanto si possa pensare (Disclaimer: io sono uno degli sviluppatori su NDepend). Finora si possono trovare namespace dipendenza ciclo out-of-the-box, ma, come mi sto spiegando qui di seguito, è facile da trovare, come pure i cicli tra tipi in uno spazio dei nomi, o metodi di un genere.

C'è un default C# LINQ code rule che elenca i cicli di dipendenza dei namespace. Tale ciclo può quindi essere esportato nel grafico delle dipendenze o nella matrice delle dipendenze. Ecco uno screenshot della regola eseguita sul codice CTP di Roslyn, giugno 2012 (notare che sono stati necessari solo 16 millisecondi). Ha trovato 11 cicli distinti, e come mostrato nella schermata, è possibile visualizzare in dettaglio ogni ciclo ed esportare un ciclo al grafico:

Rule to match namespace dependency cycle

Ecco un grafico delle dipendenze del ciclo di 7 spazi dei nomi. Si noti che sembra più complicato di un classico ciclo di O-ring. La chiave qui, è quella di uno di questi spazi dei nomi, puoi raggiungere tutti gli altri. Questa è la nozione generalizzata di ciclo (entangling).

Match namespace dependency cycle Graph

Il codice del default C# LINQ code rule che elenca i cicli di spazi dei nomi di dipendenza potrebbe apparire a prima vista scoraggiante. Ma uno sviluppatore C# dovrebbe capirlo in pochi minuti e quindi adattarlo facilmente per trovare qualsiasi tipo di ciclo di dipendenza.

Ad esempio, per trovare metodi stessi tipi cicli (anziché namespace di stessi cicli montaggio) è quasi semplice sostituzione di tutti namespace parola per metodo e assemblaggio parola per tipo.

// <Name>Avoid methods of a type to be in cycles</Name> 
warnif count > 0 

from t in Application.Types 
       .Where(t => t.ContainsMethodDependencyCycle != null && 
          t.ContainsMethodDependencyCycle.Value) 

// Optimization: restreint methods set 
// A method involved in a cycle necessarily have a null Level. 
let methodsSuspect = t.Methods.Where(m => m.Level == null) 

// hashset is used to avoid iterating again on methods already caught in a cycle. 
let hashset = new HashSet<IMethod>() 


from suspect in methodsSuspect 
    // By commenting this line, the query matches all methods involved in a cycle. 
    where !hashset.Contains(suspect) 

    // Define 2 code metrics 
    // - Methods depth of is using indirectly the suspect method. 
    // - Methods depth of is used by the suspect method indirectly. 
    // Note: for direct usage the depth is equal to 1. 
    let methodsUserDepth = methodsSuspect.DepthOfIsUsing(suspect) 
    let methodsUsedDepth = methodsSuspect.DepthOfIsUsedBy(suspect) 

    // Select methods that are both using and used by methodSuspect 
    let usersAndUsed = from n in methodsSuspect where 
         methodsUserDepth[n] > 0 && 
         methodsUsedDepth[n] > 0 
         select n 

    where usersAndUsed.Count() > 0 

    // Here we've found method(s) both using and used by the suspect method. 
    // A cycle involving the suspect method is found! 
    let cycle = usersAndUsed.Append(suspect) 

    // Fill hashset with methods in the cycle. 
    // .ToArray() is needed to force the iterating process. 
    let unused1 = (from n in cycle let unused2 = hashset.Add(n) select n).ToArray() 

select new { suspect, cycle } 

... ed ecco come il risultato di questa regola sembra (ancora con la possibilità di esportare il ciclo di metodo per il grafo delle dipendenze o matrice).Si noti che poiché il numero di metodi e tipi è molto più alto del numero di spazi dei nomi e degli assiemi, questa query ha richiesto 10 secondi per essere eseguita su un grande codebase come Roslyn (invece di 16ms per il ciclo dei nomi) quindi potrebbe essere necessario regolare il timeout di esecuzione della query CQLinq (che è 2 secondi per impostazione predefinita).

types dependency cycles

per essere completa, quello che ho notato è che il ciclo sono la maggior parte del tempo provocata da alcuni riferimenti bidirezionali (cioè A sta usando B, B sta usando A). Quindi rimuovere i riferimenti bidirezionali è la prima cosa da fare per interrompere il ciclo. Questo è il motivo per cui è stata fornita la regola CQLinq predefinita Avoid namespaces mutually dependent, che può ancora essere adattata ai cicli di tipi o metodi.