How to Use Nested Types in C

Nested types in C# are types (classes, structs, interfaces, enums, or delegates) declared directly within the scope of an enclosing class or struct. This guide provides a straightforward explanation of how to define, configure, and use nested types in C#, focusing on access modifiers, cross-scope member accessibility, instantiation patterns, and typical use cases.

Declaring a Nested Type

To create a nested type, place its declaration inside the body of the containing class.

public class Container
{
    // Nested class declaration
    public class Nested
    {
        public void DisplayMessage()
        {
            Console.WriteLine("Inside the nested class.");
        }
    }
}

Access Modifiers and Defaults

Unlike top-level types, which can only be declared as public or internal, nested types support the full range of C# access modifiers:

  • private: Accessible only within the containing type (this is the default if no modifier is specified).
  • public: Accessible to any code that has access to the containing type.
  • protected: Accessible within the containing type and types derived from the containing type.
  • internal: Accessible within the same assembly.
  • protected internal: Accessible within the same assembly or derived classes.
  • private protected: Accessible within the containing class or derived types in the same assembly.

Accessing Members Between Outer and Inner Classes

The relationship between the outer and nested classes has specific accessibility rules:

  1. Nested Class Accessing Enclosing Class Members: A nested type can access all members—including private members—of its enclosing type. However, to access instance members of the outer class, the nested class must have an explicit reference to an instance of that outer class.
public class Car
{
    private string _engineSecret = "Turbocharged V8";

    public class Engine
    {
        public void PrintEngineType(Car carInstance)
        {
            // Accessing private instance member of the outer class via reference
            Console.WriteLine(carInstance._engineSecret);
        }
    }
}
  1. Enclosing Class Accessing Nested Class Members: The enclosing class does not automatically inherit access to the private members of its nested class. It can instantiate the nested class and use any members that the nested class exposes through its own accessibility levels.

Instantiating a Nested Type

How a nested type is instantiated depends on where the code is executing and the nested type's access modifier.

  • From within the containing class:

    public class Container
    {
        public void CreateNested()
        {
            Nested item = new Nested();
            item.DisplayMessage();
        }
    
        public class Nested
        {
            public void DisplayMessage() => Console.WriteLine("Hello");
        }
    }
  • From outside the containing class: If the nested type is marked public, it is qualified using the outer type's name:

    Container.Nested nestedInstance = new Container.Nested();
    nestedInstance.DisplayMessage();

When to Use Nested Types

  • Internal Helpers: Encapsulate helper classes that only exist to serve the containing class (e.g., custom enumerators or state trackers) by declaring them private.
  • Logical Coupling: Group small, strongly bound types where the nested type has no independent conceptual meaning outside the context of the outer class.
  • Hidden Implementations: Implement interfaces explicitly or create private subclasses of abstract classes within a factory class to prevent consumers from directly creating concrete instances.