Non-static classes should be instantiated or turned to static

Rule ND1207 Object Oriented Design

A class whose constructors are never called is never instantiated and should be declared static.

Instantiation through reflection is not detected, so plug-in root classes instantiated via IoC frameworks can be false positives. Classes instantiated by ASP.NET infrastructure, view models and Entity Framework ModelSnapshot/DbContext/Migration classes are skipped. So are classes deriving from a third-party abstract class (an endpoint, a hosted service), and classes whose instance members are used by another type: a request DTO or a JSON response, created by a model binder or a serializer. Public classes are matched by default; for a framework whose clients instantiate them, uncomment !t.IsPublic.

How to fix

Investigate why the class is never instantiated. If it hosts only static methods, declare it static. If it is instantiated via reflection or by client code, adapt this rule to avoid these matches.

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>Non-static classes should be instantiated or turned to static</Name>
// <Id>ND1207:NonStaticClassesShouldBeInstantiatedOrTurnedToStatic</Id>
warnif count > 0

from t in JustMyCode.Types
where  t.IsClass &&
    //!t.IsPublic &&   // if you are developing a framework, 
                       // you might not want to match public classes
      !t.IsStatic && 
      !t.IsAbstract &&
      !t.IsDelegate &&
      !t.IsAttributeClass && // Attributes class are never seen as instantiated

       // Don't suggest to turn to static, classes that implement interfaces
       t.InterfacesImplemented.Count() == 0 &&

       // Find the first constructor of t called
       // match t if none of its constructors is called.
       t.Constructors.FirstOrDefault(ctor => ctor.NbMethodsCallingMe > 0) == null &&

       // Don't warn on Program classes generated by designers
       t.SimpleName != "Program" && 

       // Types instantiated through remoting infrastructure
      !t.DeriveFrom("System.MarshalByRefObject".AllowNoMatch()) && 
        
      // JSON, XML and binary serialized types might not be seen as instantiated.
      !t.IsUsing("Newtonsoft.Json".MatchNamespace().AllowNoMatch()) &&
      !t.IsUsing("System.Text.Json".MatchNamespace().AllowNoMatch()) &&
      !t.HasAttribute("System.Xml.Serialization.XmlRootAttribute".AllowNoMatch()) &&
      !t.IsUsing("System.Xml.Serialization.XmlElementAttribute".AllowNoMatch()) &&
      !t.IsUsing("System.Xml.Serialization.XmlAttributeAttribute".AllowNoMatch()) &&
      !t.HasAttribute ("System.SerializableAttribute".AllowNoMatch()) &&
      
      // Serialized type might never be seen as instantiated.
      !t.HasAttribute("System.Runtime.Serialization.DataContractAttribute".AllowNoMatch()) &&
      !t.IsUsing("System.Runtime.Serialization.DataMemberAttribute".AllowNoMatch()) &&

      // ASP.NET Core ViewModel and Repository
      !t.SimpleName.EndsWithAny("Model","Repository") &&
      !t.IsUsing("System.ComponentModel.DataAnnotations".AllowNoMatch().MatchNamespace()) &&

      // ASP.NET Classes that are instantiated by the ASP.NET infrastructure.
      !t.BaseClasses.Any(bc => bc.ParentNamespace.Name.StartsWithAny("System.Web", "Microsoft.AspNetCore"))  &&
      !(t.Constructors.Count() == 1 && t.Constructors.Single().Name.Contains("(IHostingEnvironment)")) &&

      // A class deriving from a third-party abstract class is a framework extension point (an endpoint,
      // a hosted service, a hub...): the framework instantiates it, and it cannot be declared static.
      !t.BaseClasses.Any(bc => bc.IsThirdParty && bc.IsAbstract)  &&

      // A class whose instance members are used by another type has instances at runtime, created outside
      // the analyzed code: by a model binder, a JSON serializer or reflection (a request DTO, a JSON response).
      !t.InstanceMethods.Any(m => m.MethodsCallingMe.Any(c => c.ParentType != t)) &&
      !t.InstanceFields.Any(f => f.MethodsUsingMe.Any(c => c.ParentType != t)) &&

      // Entity Framework ModelSnapshot and DbContext and Migration
      !t.DeriveFrom("Microsoft.EntityFrameworkCore.Infrastructure.ModelSnapshot".AllowNoMatch()) &&
      !t.DeriveFrom("Microsoft.EntityFrameworkCore.DbContext".AllowNoMatch()) &&
      !t.DeriveFrom("Microsoft.EntityFrameworkCore.Migrations.Migration".AllowNoMatch()) &&
      
      // ASP.NET Core Tag Helpers
      !t.HasAttribute("Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElementAttribute".AllowNoMatch()) &&
      !t.DeriveFrom("HtmlTags.HtmlTagTagHelper".AllowNoMatch()) &&

      // UWP Pages 
      !t.InstanceMethods.WithName("GetBindingConnector(Int32,Object)").Any() &&
      

      // Validtor classes might not be instantiated 
      !t.SimpleName.EndsWith("Validator") &&

      // Don't warn about classes instantiated by Dependency Injection frameworks
      !t.TypesUsingMe.Any(t1 => t1.TypesUsed.ParentNamespaces().Any(n => n.Name.StartsWithAny(
            "Microsoft.Extensions.DependencyInjection",
            "Autofac", "Microsoft.Practices.Unity", "Ninject",
            "StructureMap", "SimpleInjector", "Castle.Windsor",
            "LightInject", "Spring", "Lamar", "AutoMapper"
       ))) &&
       !t.BaseClasses.Any(bc => bc.FullName.StartsWith("AutoMapper")) &&

      // Don't warn if the type is used by some types using some third-party Json related types.
      // The type is potentially instantiated by a Json serializer like the GetFromJsonAsync() extension methods.
      !t.TypesUsingMe.SelectMany(t1 => t1.TypesUsed)
                     .Where(t1 => t1.IsThirdParty && t1.SimpleName.Contains("Json")).Any() &&

      // Classes that derive from Unity classes are instantiated implicitly by the Unity engine
      !t.BaseClasses.Any(bc => bc.ParentNamespace.Name.StartsWithAny("UnityEngine","UnityEditor")) &&
      
      // Don't match marker classes
      !(t.IsSealed && t.Members.Count() == 1 && 
        t.Members.Single().IsMethod && 
        t.Members.Single().AsMethod.IsConstructor &&
        t.Members.Single().AsMethod.NbParameters == 0)
      

select new { 
   t, 
   t.Visibility,
   Debt = 2.ToMinutes().ToDebt(),
   Severity = Severity.Medium
}

// Notice that classes only instantiated through reflection, 
// like plug-in root classes are matched by this rules.

//<Expl>
// The *{0}* is not instantiated in the context of the analyzed code.
// Should it be declared as a *static class* or is it a potential candidate for removal?
//</Expl>

//<Desc>
// A class whose constructors are never called is never instantiated and
// should be declared *static*.
//
// Instantiation through reflection is not detected, so plug-in root classes
// instantiated via *IoC frameworks* can be *false positives*. Classes
// instantiated by ASP.NET infrastructure, view models and Entity Framework
// ModelSnapshot/DbContext/Migration classes are skipped. So are classes
// deriving from a third-party abstract class (an endpoint, a hosted service),
// and classes whose instance members are used by another type: a request DTO
// or a JSON response, created by a model binder or a serializer. *Public*
// classes are matched by default; for a framework whose clients instantiate
// them, uncomment *!t.IsPublic*.
//</Desc>

//<HowToFix>
// Investigate why the class is never instantiated. If it hosts only static
// methods, declare it *static*. If it is instantiated via reflection or by
// client code, adapt this rule to avoid these matches.
//</HowToFix>

Object Oriented Design: all 14 items