差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

后一修订版
前一修订版
csharp:类:嵌套类 [2025/11/17 09:45] – 创建 张叶安csharp:类:嵌套类 [2025/11/27 10:21] (当前版本) – [1. 内部类需要被多处复用] 张叶安
行 1: 行 1:
-嵌套类指在一个类的内部定义的类。大多数语言(如 Java、C#)将其分为两类:+====== C# 嵌套类 (Nested Classes) 使用南 ======
  
-- **静态嵌套类**不依赖外部类实例) +嵌套类(Nested Class)是在另一个类内部声明的类。在 C# 中,嵌套类与外部类的关系主要是**访问控制**的关系,而不是对象实例的关系。
-**非静态内部类**(依赖外部类实例,可访问外部类成员)+
  
-以下是简要说明。 
  
----+**C# 与 Java 的区别**: 
 +在 C# 中,嵌套类默认**不会**自动持有外部类实例的引用(类似于 Java 的 static inner class)。如果需要访问外部类的非静态成员,必须显式传递外部类的实例。
  
-## 什么时候应该用嵌套类 +===== 一、什么时候应该用嵌套类 (最佳践) =====
-- **逻辑上只为外部类服务的类型**   +
-  例如外部类的辅助器、组成部分。 +
-- **不希望暴露给外部世界的现细节**   +
-  减少 API 表面积。 +
-- **静态嵌套类:减少外部类实例的内存占用**   +
-  如果内部类不需要访问外部类的数据,使用静态嵌套类让结构更清晰。+
  
----+嵌套类的核心使用原则是:**高内聚,低耦合**。当一个类仅仅服务于另一个类时,嵌套是最好的选择。
  
-## 什么时候不应该用嵌套类 +==== 1. 逻辑上的组成分 (强组合关系) ====
-- **内部类会被多个外部类或模块复用时**   +
-  单独作为顶级类更合理。 +
-- **嵌套层级太多导致可读性变差**   +
-  若逻辑上不属于主类,不应硬塞进去。 +
-- **非静态内类持有外部类引用可能造成内存泄漏**   +
-  尤其是异步任务、长生命周期对象。+
  
 +当子对象离开父对象就没有独立存在的意义,或者其定义仅在父对象上下文中有效时。
  
 +<code csharp>
 +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;
 +        }
 +    }
 +}
 +</code>
 +
 +**核心原理**:
 +  * **封装性**:将 `Address` 定义在 `Person` 内部,表明了它是 `Person` 的附属信息。
 +  * **命名空间整洁**:避免了全局命名空间中出现大量零散的小类(如 `PersonAddress`)。
 +
 +==== 2. 辅助器模式 (Builder / Iterator) ====
 +
 +这是嵌套类最经典的使用场景。Builder(构建者)或 Iterator(迭代器)通常需要访问外部类的私有构造函数或私有数据。
 +
 +<code csharp>
 +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);
 +        }
 +    }
 +}
 +</code>
 +
 +**核心原理**:
 +  * **访问权限**:嵌套类拥有访问外部类 `private` 成员的特权。
 +  * **单一职责**:`Builder` 的唯一职责就是构建 `User`,放在外部没有意义。
 +
 +==== 3. 需要访问外部类的私有成员 ====
 +
 +当一个辅助类需要操作主类的内部状态(私有字段),但又不想把这些字段公开(public)给全世界时。
 +
 +<code csharp>
 +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++;
 +        }
 +    }
 +}
 +</code>
 +
 +**核心原理**:
 +  * **白盒操作**:`Handler` 是 `Machine` 的“自己人”,可以安全地操作内部数据,而无需破坏 `Machine` 的封装性(即不需要把 `_state` 设为 public)。
 +
 +===== 二、什么时候不应该用嵌套类 (反模式) =====
 +
 +滥用嵌套类会导致代码难以阅读、难以测试,甚至引发内存问题。
 +
 +==== 1. 内部类需要被多处复用 ====
 +
 +如果一个类不仅被外部类使用,还被其他模块使用,它就不应该被嵌套。
 +
 +<code csharp>
 +public class Order
 +{
 +    // 错误示范:MathUtils 是通用工具,不属于 Order 独有
 +    public class MathUtils  
 +    {
 +        public static int Add(int a, int b) => a + b;
 +    }
 +}
 +</code>
 +
 +
 +**后果**:其他类如果要用这个工具,必须写成 `Order.MathUtils.Add(...)`,这不仅啰嗦,而且让人困惑:为什么做加法运算需要依赖 `Order` 类?
 +
 +
 +==== 2. 生命周期管理风险 (内存泄漏) ====
 +
 +如果嵌套类的实例生命周期比外部类长,且嵌套类持有了外部类的引用,会导致外部类无法被垃圾回收(GC)。
 +
 +<code csharp>
 +public class Controller
 +{
 +    // 假设这是一个很大的对象,占用大量内存
 +    private byte[] _largeData = new byte[1024 * 1024]; 
 +
 +    public class BackgroundWorker 
 +    {
 +        public Controller Parent { get; set; }
 +        
 +        public void Run() {
 +            // 模拟长时间运行的任务
 +            while(true) { /* ... */ }
 +        }
 +    }
 +}
 +</code>
 +
 +**核心原理**:
 +  * 如果 `BackgroundWorker` 被单独传递给一个线程池运行,而它又引用了 `Controller`,那么只要任务没结束,巨大的 `Controller` 对象就永远无法释放。
 +  * **建议**:对于长期运行的后台任务,尽量使用独立的类,或者确保不持有外部类的强引用(使用 `WeakReference`)。
 +
 +==== 3. 嵌套层级过深 ====
 +
 +<code csharp>
 +public class A
 +{
 +    public class B
 +    {
 +        public class C
 +        {
 +            public class D { } // A.B.C.D
 +        }
 +    }
 +}
 +</code>
 +
 +**核心原理**:
 +  * **可读性灾难**:这种代码被称为“俄罗斯套娃”代码,极难阅读和维护。
 +  * **重构困难**:如果将来要把 `D` 移出来,所有引用它的代码都需要修改。
 +
 +==== 4. 逻辑关系松散 ====
 +
 +仅仅为了“整理代码”而把不相关的类塞进去是不对的。
 +
 +<code csharp>
 +public class Company
 +{
 +    // 错误示范:Logger 是通用的基础设施,不是 Company 的业务组成
 +    public class Logger  
 +    {
 +        public void Log(string message) => Console.WriteLine(message);
 +    }
 +}
 +</code>
 +
 +**核心原理**:
 +  * **单一职责原则 (SRP)**:`Company` 应该只负责公司的业务逻辑,不应该负责定义日志工具。`Logger` 应该是一个独立的类或接口。
 +
 +===== 总结 =====
 +
 +^ 维度 ^ 建议嵌套 ^ 建议独立 ^
 +| **复用性** | 仅被外部类使用 | 被多个模块使用 |
 +| **访问权限** | 需要访问外部类 private 成员 | 仅访问 public 成员 |
 +| **生命周期** | 与外部类共存亡 | 独立于外部类存在 (如后台任务) |
 +| **逻辑关系** | 是外部类的一部分 (Is-Part-Of) | 与外部类无关 (工具类/通用服务) |

该主题尚不存在

您访问的页面并不存在。如果允许,您可以使用创建该页面按钮来创建它。

  • csharp/类/嵌套类.1763343911.txt.gz
  • 最后更改: 2025/11/17 09:45
  • 张叶安