Avoid mutually-dependent types
Matches pairs of classes or structures using each other: neither can be reviewed, refactored or tested without the other.
Disabled by default: mutual dependency is legitimate in common situations like recursive data structures, complex serialization or the visitor pattern. Enable it if you wish to fix most of its issues.
Disabled by default: mutual dependency is legitimate in common situations like recursive data structures, complex serialization or the visitor pattern. Enable it if you wish to fix most of its issues.
How to fix
Identify the unwanted dependency (t1 using t2, or the reverse) and remove it, often via the dependency inversion principle — interfaces abstracting one implementation from the other: https://blog.ndepend.com/solid-design-the-dependency-inversion-principle-dip
CQLinq source
This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.
// <Name>Avoid mutually-dependent types</Name>
// <Id>ND1409:AvoidMutuallyDependentTypes</Id>
warnif count > 0
from t1 in Application.Types
where t1.NbLinesOfCode > 10 &&
(t1.IsClass || t1.IsStructure)
from t2 in t1.TypesUsingMe
where t2.NbLinesOfCode > 10 &&
(t2.IsClass || t2.IsStructure) &&
// The mutual check — this is what keeps test types out of the pairs.
t1.IsUsing(t2)
// To avoid reporting twice the same pair
// use lexicographically sort of full names of types
// to only take the first one.
where new string[] {t1.FullName, t2.FullName}.OrderBy(x => x).First() == t1.FullName
// So far we only keep dependencies that involve method call.
// It is possible to comment the two 'where' clauses below to get also
// others kind of dependencies, like usage of typeof(Class).
let t2MethodsUsingT1 = t2.Methods.Where(m => m.IsUsing(t1)).ToArray()
where t2MethodsUsingT1.Length > 0
let t1MethodsUsingT2 = t1.Methods.Where(m => m.IsUsing(t2)).ToArray()
where t1MethodsUsingT2.Length > 0
select new {
t1, t2,
t2MethodsUsingT1,
t1MethodsUsingT2,
Debt = 20.ToMinutes().ToDebt(),
Severity = Severity.Medium
}
//<Expl>
// The *{0}* and the *{1}* are mutually dependent.
// To address this, identify the direction in which to apply
// the *dependency inversion principle* and make the necessary adjustments.
//</Expl>
//<Desc>
// Matches pairs of classes or structures using each other: neither can be
// reviewed, refactored or tested without the other.
//
// Disabled by default: mutual dependency is legitimate in common situations
// like recursive data structures, complex serialization or the visitor
// pattern. Enable it if you wish to fix most of its issues.
//</Desc>
//<HowToFix>
// Identify the unwanted dependency (*t1* using *t2*, or the reverse) and
// remove it, often via the **dependency inversion principle** — interfaces
// abstracting one implementation from the other:
// https://blog.ndepend.com/solid-design-the-dependency-inversion-principle-dip
//</HowToFix>
Open in the interactive Rules Explorer · All default rules
Architecture: all 13 items
- Avoid namespaces mutually dependent ND1400
- Avoid namespaces dependency cycles ND1401
- Avoid partitioning the code base through many small library Assemblies ND1402
- Enforcing Clean Architecture ND1412
- UI layer shouldn't use directly DB types ND1403
- UI layer shouldn't use directly DAL layer ND1404
- Assemblies with poor cohesion (RelationalCohesion) ND1405
- Namespaces with poor cohesion (RelationalCohesion) ND1406
- Assemblies that don't satisfy the Abstractness/Instability principle ND1407
- Avoid excessive class coupling ND1410
- Higher cohesion - lower coupling ND1408
- Avoid mutually-dependent types ND1409
- Example of custom rule to check for dependency ND9900