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:
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).
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).
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.
Utilità correlata per trovare rapidamente riferimenti circolari: http://stackoverflow.com/a/43374622/64334 –