目录

C# 嵌套类 (Nested Classes) 使用指南

嵌套类(Nested Class)是在另一个类内部声明的类。在 C# 中,嵌套类与外部类的关系主要是访问控制的关系,而不是对象实例的关系。

C# 与 Java 的区别: 在 C# 中,嵌套类默认不会自动持有外部类实例的引用(类似于 Java 的 static inner class)。如果需要访问外部类的非静态成员,必须显式传递外部类的实例。

一、什么时候应该用嵌套类 (最佳实践)

嵌套类的核心使用原则是:高内聚,低耦合。当一个类仅仅服务于另一个类时,嵌套是最好的选择。

1. 逻辑上的组成部分 (强组合关系)

当子对象离开父对象就没有独立存在的意义,或者其定义仅在父对象上下文中有效时。

public class Person
{
    public string Name { get; }
    public Address HomeAddress { get; }
 
    public Person(string name, Address homeAddress)
    {
        Name = name;
        HomeAddress = homeAddress;
    }
 
    // 嵌套类:Address 被视为 Person 的一部分
    public class Address 
    {
        public string City { get; }
        public string Street { get; }
 
        public Address(string city, string street)
        {
            City = city;
            Street = street;
        }
    }
}

核心原理

2. 辅助器模式 (Builder / Iterator)

这是嵌套类最经典的使用场景。Builder(构建者)或 Iterator(迭代器)通常需要访问外部类的私有构造函数或私有数据。

public class User
{
    public string Name { get; }
    public int Age { get; }
 
    // 私有构造函数:强制外部必须通过 Builder 来创建实例
    private User(string name, int age)
    {
        Name = name;
        Age = age;
    }
 
    public class Builder
    {
        private string _name;
        private int _age;
 
        public Builder SetName(string name)
        {
            _name = name;
            return this; // 链式调用
        }
 
        public Builder SetAge(int age)
        {
            _age = age;
            return this;
        }
 
        public User Build()
        {
            // 嵌套类可以访问外部类的 private 构造函数
            return new User(_name, _age);
        }
    }
}

核心原理

3. 需要访问外部类的私有成员

当一个辅助类需要操作主类的内部状态(私有字段),但又不想把这些字段公开(public)给全世界时。

public class Machine
{
    private int _state = 0; // 私有状态
 
    public class Handler
    {
        private readonly Machine _machine;
 
        // C# 嵌套类不自动持有外部引用,需要手动传入
        public Handler(Machine machine)
        {
            _machine = machine;
        }
 
        public void Increase()
        {
            // 关键点:可以直接访问外部类的 private 字段 _state
            _machine._state++;
        }
    }
}

核心原理

二、什么时候不应该用嵌套类 (反模式)

滥用嵌套类会导致代码难以阅读、难以测试,甚至引发内存问题。

1. 内部类需要被多处复用

如果一个类不仅被外部类使用,还被其他模块使用,它就不应该被嵌套。

public class Order
{
    // 错误示范:MathUtils 是通用工具,不属于 Order 独有
    public class MathUtils  
    {
        public static int Add(int a, int b) => a + b;
    }
}

后果:其他类如果要用这个工具,必须写成 `Order.MathUtils.Add(…)`,这不仅啰嗦,而且让人困惑:为什么做加法运算需要依赖 `Order` 类?

2. 生命周期管理风险 (内存泄漏)

如果嵌套类的实例生命周期比外部类长,且嵌套类持有了外部类的引用,会导致外部类无法被垃圾回收(GC)。

public class Controller
{
    // 假设这是一个很大的对象,占用大量内存
    private byte[] _largeData = new byte[1024 * 1024]; 
 
    public class BackgroundWorker 
    {
        public Controller Parent { get; set; }
 
        public void Run() {
            // 模拟长时间运行的任务
            while(true) { /* ... */ }
        }
    }
}

核心原理

3. 嵌套层级过深

public class A
{
    public class B
    {
        public class C
        {
            public class D { } // A.B.C.D
        }
    }
}

核心原理

4. 逻辑关系松散

仅仅为了“整理代码”而把不相关的类塞进去是不对的。

public class Company
{
    // 错误示范:Logger 是通用的基础设施,不是 Company 的业务组成
    public class Logger  
    {
        public void Log(string message) => Console.WriteLine(message);
    }
}

核心原理

总结

维度 建议嵌套 建议独立
复用性 仅被外部类使用 被多个模块使用
访问权限 需要访问外部类 private 成员 仅访问 public 成员
生命周期 与外部类共存亡 独立于外部类存在 (如后台任务)
逻辑关系 是外部类的一部分 (Is-Part-Of) 与外部类无关 (工具类/通用服务)