Types that could be declared as private, nested in a parent type

Rule ND1803 Visibility

Matches types that could be nested and declared private in another type: the parent type is the only consumer, and both are in the same namespace. This improves encapsulation by minimizing the type's consumption scope. Classes with extension methods cannot be nested and are not matched.

How to fix

Nest each matched type into the specified parent type as private. Nested private types are hardly testable though: skip types consumed directly by tests.

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>Types that could be declared as private, nested in a parent type</Name>
// <Id>ND1803:TypesThatCouldBeDeclaredAsPrivateNestedInAParentType</Id>

warnif count > 0 
from t in JustMyCode.Types
where !t.IsGeneratedByCompiler &&
      !t.IsNested &&
      !t.IsPubliclyVisible &&
      !t.IsEnumeration &&
      // Don't warn on types declared with 'file' visibility
      !t.IsVisibleOnlyInFile &&
       // Only one type user… TypesUsingMe spans the test code too: a type consumed by
       // a test type as well as by its parent has two users and is NOT proposed for nesting
       t.TypesUsingMe.Count() == 1 &&
       // Require that t doesn't contain any extension method.
       // Types with extension methods cannot be nested.
       !t.Methods.Any(m => m.IsExtensionMethod)

// The single user is a test type only when nothing else consumes t and that test lives in
// the same namespace (rare): the advice then names a test class as the parent — accepted.
let couldBeNestedIn = t.TypesUsingMe.Single()
where !couldBeNestedIn.IsGeneratedByCompiler &&
      !couldBeNestedIn.IsInterface && // Cannot nest a type in an interface
       // …declared in the same namespace
       couldBeNestedIn.ParentNamespace == t.ParentNamespace &&

       // Don't advise to move a base class
       // or an interface into one of its child type.
      !couldBeNestedIn.DeriveFrom(t) &&
      !couldBeNestedIn.Implement(t)

select new { 
   t, 
   couldBeNestedIn,
   Debt = 3.ToMinutes().ToDebt(), // It is fast to nest a type into another one
   Severity = Severity.Low       // This rule proposes advices, not potential problems
}

//<Expl>
// The *{0}* is only used by the *{1}* and seems to be an implementation detail of it.
// Consider declaring this type as *private* and *nested* into its parent type.
// Doing so **improves encapsulation** by constraining this type's accessibility 
// to the smallest possible scope.
//</Expl>

//<Desc>
// Matches types that could be *nested* and declared *private* in another
// type: the *parent type* is the only consumer, and both are in the same
// namespace. This **improves encapsulation** by minimizing the type's
// consumption scope. Classes with extension methods cannot be nested and
// are not matched.
//</Desc>

//<HowToFix>
// Nest each matched *type* into the specified *parent type* as private.
// *Nested private types* are hardly testable though: skip types consumed
// directly by tests.
//</HowToFix>

Visibility: all 12 items