目 录CONTENT

文章目录

CSharp(三十)嵌套类(Nested Class)详解

CSharp(三十)嵌套类(Nested Class)详解


目录

  1. 什么是嵌套类
  2. 为什么需要嵌套类
  3. 嵌套类的基本语法
  4. 访问规则详解
  5. public 嵌套类 vs private 嵌套类
  6. 嵌套类的访问修饰符
  7. 静态嵌套类 vs 实例嵌套类
  8. 多层嵌套
  9. 典型使用场景
  10. 嵌套类 vs 独立类
  11. 常见误区与注意事项
  12. 综合示例

1. 什么是嵌套类

嵌套类(Nested Class) 就是一个定义在另一个类内部的类。外层的类称为外部类(Outer Class),内层的类称为嵌套类

通俗理解:把外部类比作一个"大盒子",嵌套类就是放在这个大盒子里的"小盒子"。这个小盒子虽然在物理上在大盒子里面,但它本身也是一个完整的类,可以有自己的字段、属性、方法。

┌─────────────────────────────────┐
│  外部类 (Outer)                  │
│                                 │
│   ┌─────────────────────────┐   │
│   │  嵌套类 (Inner)          │   │
│   │  - 字段、属性、方法       │   │
│   │  - 构造函数               │   │
│   └─────────────────────────┘   │
│                                 │
└─────────────────────────────────┘

最简单的例子

public class School          // 外部类
{
    public class Classroom   // 嵌套类 — 定义在 School 内部
    {
        public string RoomNumber { get; set; }
        public int Capacity { get; set; }

        public void DisplayInfo()
        {
            Console.WriteLine($"教室 {RoomNumber},容纳 {Capacity} 人");
        }
    }
}

2. 为什么需要嵌套类

嵌套类最核心的用途是:将"只有外部类才用得着"的辅助类隐藏在外部类内部

2.1 实际场景举例

假设你正在写一个 LinkedList(链表)类,链表内部需要一个 Node(节点)类来表示每个元素。这个 Node 类对于使用链表的开发者来说没有任何意义——他们只需要 LinkedList.Add()LinkedList.Remove() 这些方法,完全不需要知道 Node 类的存在。

这时就可以把 Node 作为 LinkedList私有嵌套类

public class LinkedList
{
    // 使用者不需要知道 Node 的存在,所以用 private
    private class Node
    {
        public int Value;
        public Node Next;

        public Node(int value)
        {
            Value = value;
            Next = null;
        }
    }

    private Node head;  // 外部类像使用普通类型一样使用嵌套类

    public void Add(int value)
    {
        Node newNode = new Node(value);
        if (head == null)
        {
            head = newNode;
        }
        else
        {
            Node current = head;
            while (current.Next != null)
                current = current.Next;
            current.Next = newNode;
        }
    }

    public void PrintAll()
    {
        Node current = head;
        while (current != null)
        {
            Console.Write(current.Value + " ");
            current = current.Next;
        }
        Console.WriteLine();
    }
}

// 使用链表时,你根本看不到 Node:
var list = new LinkedList();
list.Add(1);
list.Add(2);
list.Add(3);
list.PrintAll();  // 输出:1 2 3

2.2 使用嵌套类的好处

好处 说明
封装细节 把实现细节藏在外部类里面,外部调用者看不到,接口更干净
命名清晰 LinkedList.NodeLinkedListNode 更能表达"这个类是专属于 LinkedList 的"
避免命名冲突 两个不同的外部类可以有同名的私有嵌套类,互不影响
逻辑分组 相关代码放在一起,方便维护和理解

3. 嵌套类的基本语法

// 定义嵌套类的语法
public class 外部类
{
    // 可以是 public、private、protected 等
    public class 嵌套类
    {
        // 和普通类一模一样,可以写字段、属性、方法、构造函数等
    }
}

3.1 完整示例

public class Order               // 外部类:订单
{
    public int OrderId { get; set; }
    public string Customer { get; set; }
    public decimal TotalAmount { get; private set; }

    // 嵌套类:订单明细项
    public class OrderItem
    {
        public string ProductName { get; set; }
        public decimal Price { get; set; }
        public int Quantity { get; set; }

        // 嵌套类也可以有构造函数
        public OrderItem(string productName, decimal price, int quantity)
        {
            ProductName = productName;
            Price = price;
            Quantity = quantity;
        }

        // 嵌套类可以有方法
        public decimal GetSubTotal()
        {
            return Price * Quantity;
        }

        public override string ToString()
        {
            return $"{ProductName} × {Quantity} = {GetSubTotal():C}";
        }
    }

    // 保存明细项的列表
    private List<OrderItem> items = new List<OrderItem>();

    // 外部类使用嵌套类
    public void AddItem(string name, decimal price, int quantity)
    {
        items.Add(new OrderItem(name, price, quantity));
        TotalAmount += price * quantity;
    }

    public void PrintOrder()
    {
        Console.WriteLine($"订单号:{OrderId},客户:{Customer}");
        Console.WriteLine("明细:");
        foreach (var item in items)
        {
            Console.WriteLine($"  {item}");
        }
        Console.WriteLine($"总计:{TotalAmount:C}");
    }
}

3.2 如何在外部使用嵌套类

如果嵌套类是 public 的,外部代码也可以创建嵌套类的实例,但需要通过外部类名来引用

// ✅ 正确:通过"外部类.嵌套类"访问
Order.OrderItem item = new Order.OrderItem("键盘", 299, 2);

// ❌ 错误:不能直接使用嵌套类名
// OrderItem item = new OrderItem("键盘", 299, 2);   // 这样会报错!

记忆技巧:嵌套类在全名是 外部类名.嵌套类名。就像文件路径一样,Order 文件夹下的 OrderItem 文件,要写成 Order.OrderItem


4. 访问规则详解

这是嵌套类最容易混淆的地方,需要仔细理解。

4.1 规则一:嵌套类可以访问外部类的私有成员

嵌套类虽然是独立的类,但由于它"住"在外部类内部,所以嵌套类可以访问外部类的私有成员

public class BankAccount
{
    private decimal balance = 1000;          // 外部类的 private 字段
    private string secretKey = "ABC123";     // 外部类的 private 字段

    // 嵌套类
    public class AccountLogger
    {
        // 注意:这里需要传入外部类实例才能访问实例成员
        public void LogBalance(BankAccount account)
        {
            // ✅ 可以!嵌套类能访问外部类的 private 成员
            Console.WriteLine($"当前余额:{account.balance}");
            Console.WriteLine($"密钥:{account.secretKey}");
        }
    }

    public void Test()
    {
        var logger = new AccountLogger();
        logger.LogBalance(this);  // 传入当前实例
    }
}

// 使用
var account = new BankAccount();
account.Test();
// 输出:
// 当前余额:1000
// 密钥:ABC123

重点:嵌套类访问的是外部类实例的私有成员,所以需要传入外部类的实例引用。静态成员则可以直接访问。

4.2 规则二:外部类可以访问嵌套类的私有成员

反过来,外部类也能访问嵌套类的私有成员(因为它们本质上在同一"家庭"里)。

public class Outer
{
    private class Inner
    {
        private string secret = "内部秘密";
        private int innerId = 42;
    }

    public void RevealSecret()
    {
        Inner inner = new Inner();

        // ✅ 可以!外部类能访问嵌套类的 private 成员
        Console.WriteLine($"我看到了:{inner.secret}");
        Console.WriteLine($"ID:{inner.innerId}");
    }
}

4.3 规则三:外界无法访问私有嵌套类

如果嵌套类是 private 的,外部世界完全看不到它,连创建实例都不行。

public class Outer
{
    private class SecretHelper
    {
        public void DoWork()
        {
            Console.WriteLine("我在工作...");
        }
    }

    // 外部类内部可以正常使用
    public void UseHelper()
    {
        SecretHelper helper = new SecretHelper();
        helper.DoWork();
    }
}

// 外部使用:
var outer = new Outer();
outer.UseHelper();  // ✅ 可以,通过外部类间接使用

// SecretHelper helper = new Outer.SecretHelper();  // ❌ 编译错误!访问不到

4.4 访问规则速查表

访问方向 public 嵌套类 private 嵌套类
嵌套类 → 外部类的 private 成员
外部类 → 嵌套类的 private 成员
外界 → 嵌套类(直接创建实例)
外界 → 嵌套类的 private 成员
同程序集的其他类 → 嵌套类的 internal 成员

5. public 嵌套类 vs private 嵌套类

这是使用嵌套类时最重要的选择:该用 public 还是 private

5.1 private 嵌套类(最常用)

场景:嵌套类只是外部类的内部实现细节,外部调用者完全不需要知道。

public class MusicPlayer
{
    // private 嵌套类:播放列表内部数据结构,用户不需要知道
    private class SongNode
    {
        public string Title;
        public string Artist;
        public SongNode Previous;
        public SongNode Next;

        public SongNode(string title, string artist)
        {
            Title = title;
            Artist = artist;
        }
    }

    private SongNode current;
    private SongNode first;

    public void AddSong(string title, string artist)
    {
        SongNode node = new SongNode(title, artist);
        if (first == null)
        {
            first = current = node;
        }
        else
        {
            SongNode last = first;
            while (last.Next != null)
                last = last.Next;
            last.Next = node;
            node.Previous = last;
        }
    }

    public void Play()
    {
        if (current != null)
            Console.WriteLine($"正在播放:{current.Title} - {current.Artist}");
    }
}

// 使用者视角:完全看不到 SongNode,接口干净
var player = new MusicPlayer();
player.AddSong("夜曲", "周杰伦");
player.Play();

5.2 public 嵌套类

场景:嵌套类虽然逻辑上属于外部类,但外部代码也需要用到它。

public class Color
{
    public int R { get; set; }
    public int G { get; set; }
    public int B { get; set; }

    public Color(int r, int g, int b)
    {
        R = r; G = g; B = b;
    }

    // public 嵌套类:预定义颜色常量
    // 外部代码可以用 Color.Preset.Red 来获取红色
    public static class Preset
    {
        public static Color Red => new Color(255, 0, 0);
        public static Color Green => new Color(0, 255, 0);
        public static Color Blue => new Color(0, 0, 255);
        public static Color Black => new Color(0, 0, 0);
        public static Color White => new Color(255, 255, 255);
    }
}

// 外部使用:
Color red = Color.Preset.Red;     // 自然地通过 Color.Preset 访问
Color blue = Color.Preset.Blue;
Console.WriteLine($"红:R={red.R}");  // 红:R=255

5.3 选择建议

问题 private public
嵌套类只是外部类的内部实现细节 -
外部代码需要创建嵌套类的实例 -
嵌套类有自己的公开 API -
不确定该用哪个 -

经验法则:有疑问时,先用 private。等真正有外部使用需求时再改成 public,改起来很容易。


6. 嵌套类的访问修饰符

嵌套类可以拥有普通类不能使用的访问修饰符:

public class Outer
{
    // ====== 嵌套类可以用这五种访问修饰符 ======

    // 1. public:任何地方都能访问
    public class PublicNested { }

    // 2. private:只有外部类内部能访问(最常用)
    private class PrivateNested { }

    // 3. protected:外部类和派生类能访问
    protected class ProtectedNested { }

    // 4. internal:同一程序集内能访问
    internal class InternalNested { }

    // 5. protected internal:同一程序集或派生类能访问
    protected internal class ProtectedInternalNested { }
}

注意:普通顶层类只能用 publicinternal,而嵌套类因为坐在另一个类里面,所以可以用全部五种修饰符。

protected 嵌套类的实际应用

public class BaseController
{
    // protected 嵌套类:只让派生类使用
    protected class ValidationResult
    {
        public bool IsValid { get; set; }
        public string ErrorMessage { get; set; }

        public static ValidationResult Success()
            => new ValidationResult { IsValid = true };

        public static ValidationResult Fail(string message)
            => new ValidationResult { IsValid = false, ErrorMessage = message };
    }
}

// 派生类可以访问受保护的嵌套类
public class UserController : BaseController
{
    public void CreateUser(string name, string email)
    {
        ValidationResult result;

        if (string.IsNullOrEmpty(name))
            result = ValidationResult.Fail("姓名不能为空");
        else if (string.IsNullOrEmpty(email))
            result = ValidationResult.Fail("邮箱不能为空");
        else
            result = ValidationResult.Success();

        if (result.IsValid)
            Console.WriteLine("用户创建成功");
        else
            Console.WriteLine($"创建失败:{result.ErrorMessage}");
    }
}

// 外部类不能访问 protected 嵌套类
// var vr = new BaseController.ValidationResult();  // ❌ 编译错误!

7. 静态嵌套类 vs 实例嵌套类

7.1 实例嵌套类

不需要外部类实例就能创建,但它是一个普通类,有自己的实例。

public class Car
{
    public string Model { get; set; }

    // 实例嵌套类:需要创建自己的实例
    public class Engine
    {
        public int HorsePower { get; set; }
        public string Type { get; set; }

        public void Start()
        {
            Console.WriteLine($"{HorsePower}马力 {Type} 引擎启动!");
        }
    }
}

// 使用:
Car.Engine engine = new Car.Engine();  // 不需要 Car 的实例
engine.HorsePower = 200;
engine.Type = "V6";
engine.Start();  // 输出:200马力 V6 引擎启动!

7.2 静态嵌套类

使用 static 修饰,只能包含静态成员,不需要实例化。

public class Calculator
{
    // 静态嵌套类:全是静态方法,不需要 new
    public static class Constants
    {
        public static double Pi => 3.1415926;
        public static double E => 2.7182818;

        public static double ConvertToDegrees(double radians)
        {
            return radians * 180 / Pi;
        }
    }

    // 静态嵌套类作为分组工具
    public static class Trigonometry
    {
        public static double Sin(double angle)
        {
            return Math.Sin(angle);
        }

        public static double Cos(double angle)
        {
            return Math.Cos(angle);
        }
    }
}

// 使用:不需要 new,直接用 外部类.嵌套类.方法
double area = Calculator.Constants.Pi * 5 * 5;
double degrees = Calculator.Constants.ConvertToDegrees(2 * Calculator.Constants.Pi);
double sinValue = Calculator.Trigonometry.Sin(Calculator.Constants.Pi / 2);

Console.WriteLine($"圆的面积:{area:F2}");        // 78.54
Console.WriteLine($"2π 弧度 = {degrees:F2}°");    // 360.00°
Console.WriteLine($"sin(π/2) = {sinValue}");      // 1

7.3 对照表

特性 实例嵌套类 静态嵌套类
声明方式 public class Xxx public static class Xxx
能否有实例字段 ✅ 可以有 ❌ 只能有静态字段
是否需要 new ✅ 需要 ❌ 不需要
能否有实例构造函数 ✅ 可以有 ❌ 不能有(静态类不能有构造函数)
典型用途 辅助数据结构(Node, Item, Record 等) 常量组、工具方法组、枚举值的逻辑分组

8. 多层嵌套

嵌套类内部还可以再嵌套类,不过一般不建议超过两层(超过两层会很难读):

public class Company
{
    public string Name { get; set; }

    // 第一层嵌套
    public class Department
    {
        public string DeptName { get; set; }

        // 第二层嵌套
        public class Team
        {
            public string TeamName { get; set; }
            public int MemberCount { get; set; }

            public void Display()
            {
                Console.WriteLine($"团队:{TeamName},成员:{MemberCount}人");
            }
        }
    }
}

// 使用时要一层一层写全名
var team = new Company.Department.Team();
team.TeamName = "后端开发组";
team.MemberCount = 8;
team.Display();  // 团队:后端开发组,成员:8人

建议:一般只用一层嵌套就够了。如果需要二层甚至三层,考虑是不是该重构为独立的类或使用命名空间来组织。


9. 典型使用场景

9.1 场景一:链表 / 树的节点

public class BinarySearchTree
{
    // 树节点:外部使用者不需要知道树是怎么构成的
    private class TreeNode
    {
        public int Value;
        public TreeNode Left;
        public TreeNode Right;

        public TreeNode(int value)
        {
            Value = value;
            Left = Right = null;
        }
    }

    private TreeNode root;

    public void Insert(int value)
    {
        root = InsertRec(root, value);
    }

    private TreeNode InsertRec(TreeNode node, int value)
    {
        if (node == null)
            return new TreeNode(value);

        if (value < node.Value)
            node.Left = InsertRec(node.Left, value);
        else if (value > node.Value)
            node.Right = InsertRec(node.Right, value);

        return node;
    }

    public bool Search(int value)
    {
        TreeNode current = root;
        while (current != null)
        {
            if (value == current.Value)
                return true;
            current = value < current.Value ? current.Left : current.Right;
        }
        return false;
    }

    // 中序遍历(从小到大输出)
    public void PrintInOrder()
    {
        InOrderTraversal(root);
        Console.WriteLine();
    }

    private void InOrderTraversal(TreeNode node)
    {
        if (node != null)
        {
            InOrderTraversal(node.Left);
            Console.Write(node.Value + " ");
            InOrderTraversal(node.Right);
        }
    }
}

// 使用示例:
var bst = new BinarySearchTree();
bst.Insert(50);
bst.Insert(30);
bst.Insert(70);
bst.Insert(20);
bst.Insert(40);

bst.PrintInOrder();                   // 20 30 40 50 70
Console.WriteLine(bst.Search(30));    // True
Console.WriteLine(bst.Search(100));   // False

9.2 场景二:Builder(构建器)模式

public class SqlQuery
{
    public string SelectClause { get; private set; }
    public string FromClause { get; private set; }
    public string WhereClause { get; private set; }
    public string OrderByClause { get; private set; }

    // 构建器作为嵌套类
    public class Builder
    {
        private SqlQuery query = new SqlQuery();

        public Builder Select(string columns)
        {
            query.SelectClause = $"SELECT {columns}";
            return this;
        }

        public Builder From(string table)
        {
            query.FromClause = $"FROM {table}";
            return this;
        }

        public Builder Where(string condition)
        {
            query.WhereClause = $"WHERE {condition}";
            return this;
        }

        public Builder OrderBy(string orderBy)
        {
            query.OrderByClause = $"ORDER BY {orderBy}";
            return this;
        }

        public SqlQuery Build()
        {
            return query;
        }
    }

    public string GetSql()
    {
        return $"{SelectClause} {FromClause} {WhereClause} {OrderByClause}".Trim();
    }
}

// 使用:链式调用构建 SQL
var query = new SqlQuery.Builder()
    .Select("Name, Age, Email")
    .From("Users")
    .Where("Age > 18")
    .OrderBy("Name ASC")
    .Build();

Console.WriteLine(query.GetSql());
// 输出:SELECT Name, Age, Email FROM Users WHERE Age > 18 ORDER BY Name ASC

9.3 场景三:枚举或常量的逻辑分组

public class GameSettings
{
    // 用静态嵌套类分组相关常量
    public static class Audio
    {
        public static float MasterVolume = 1.0f;
        public static float MusicVolume = 0.8f;
        public static float SfxVolume = 1.0f;

        public static void Mute()
        {
            MasterVolume = 0;
            Console.WriteLine("已静音");
        }
    }

    public static class Graphics
    {
        public static int ResolutionWidth = 1920;
        public static int ResolutionHeight = 1080;
        public static int FrameRate = 60;
        public static bool Vsync = true;

        public static string GetResolution()
        {
            return $"{ResolutionWidth}×{ResolutionHeight}@{FrameRate}fps";
        }
    }

    public static class Controls
    {
        public static float MouseSensitivity = 1.5f;
        public static bool InvertY = false;
    }
}

// 使用:清晰的层级结构
GameSettings.Audio.Mute();
Console.WriteLine(GameSettings.Graphics.GetResolution());  // 1920×1080@60fps
GameSettings.Controls.MouseSensitivity = 2.0f;

9.4 场景四:结果包装类

public class NetworkRequest
{
    // 用嵌套类封装请求结果,外部使用时语义清晰
    public class Result
    {
        public bool Success { get; set; }
        public string Data { get; set; }
        public int StatusCode { get; set; }
        public string ErrorMessage { get; set; }

        public static Result Ok(string data)
            => new Result { Success = true, Data = data, StatusCode = 200 };

        public static Result Fail(int statusCode, string error)
            => new Result { Success = false, StatusCode = statusCode, ErrorMessage = error };
    }

    public Result Fetch(string url)
    {
        // 模拟网络请求
        if (string.IsNullOrEmpty(url))
            return Result.Fail(400, "URL 不能为空");

        if (url.Contains("error"))
            return Result.Fail(500, "服务器内部错误");

        return Result.Ok($"从 {url} 获取的数据...");
    }
}

// 使用:
var request = new NetworkRequest();

var result1 = request.Fetch("https://api.example.com/users");
if (result1.Success)
    Console.WriteLine($"成功:{result1.Data}");
else
    Console.WriteLine($"失败 [{result1.StatusCode}]:{result1.ErrorMessage}");

var result2 = request.Fetch("");
if (!result2.Success)
    Console.WriteLine($"失败 [{result2.StatusCode}]:{result2.ErrorMessage}");

10. 嵌套类 vs 独立类

什么时候用嵌套类?

情况 用嵌套类 用独立类
辅助类只在一个类内部使用 -
辅助类和主类有紧密逻辑关系 -
想对外隐藏实现细节 -
辅助类需要被多个类共享使用 -
辅助类逻辑复杂,单独维护更好理解 -
辅助类可能在未来被其他模块复用 -
为了用嵌套类作为命名空间组织代码(Java 风格) -

核心判断标准:问自己"这个辅助类离开外部类还有独立价值吗?"如果答案是"没有",就用嵌套类。


11. 常见误区与注意事项

误区一:嵌套类会自动持有外部类实例

public class Outer
{
    public string Name = "外部";
    public int Value = 100;

    public class Inner
    {
        public void Test()
        {
            // ❌ 错误!Inner 没有自动持有 Outer 的引用
            // Console.WriteLine(Name);      // 编译错误:Name 不存在
            // Console.WriteLine(Value);     // 编译错误:Value 不存在
        }
    }
}

C# 的嵌套类和 Java 的"内部类"不同。Java 的非静态内部类会自动持有外部类引用,但 C# 的嵌套类不会。在 C# 中要访问外部类实例成员,必须显式传入外部类引用。

public class Outer
{
    public string Name = "外部";

    public class Inner
    {
        // ✅ 正确:通过参数接收外部类实例
        public void Test(Outer outer)
        {
            Console.WriteLine(outer.Name);  // 可以访问了
        }
    }
}

误区二:嵌套类可以访问外部类的实例成员(没有实例引用)

public class Outer
{
    private int instanceField = 42;
    private static int staticField = 100;

    public class Inner
    {
        public void Demo()
        {
            // ❌ 错误:不能直接访问实例成员
            // Console.WriteLine(instanceField);

            // ✅ 可以:静态成员属于类本身,不需要实例
            Console.WriteLine($"静态字段:{staticField}");
        }

        public void Demo2(Outer outer)
        {
            // ✅ 可以:有实例引用就能访问
            Console.WriteLine($"实例字段:{outer.instanceField}");
        }
    }
}

误区三:过度使用嵌套类

// ❌ 不好的做法:三层甚至更多层的嵌套,代码很难读
public class A
{
    public class B
    {
        public class C
        {
            public class D  // 太难读了!
            {
                public void DoSomething() { }
            }
        }
    }
}

// 使用时:
var d = new A.B.C.D();
d.DoSomething();  // 虽然能工作,但层级太深让人头晕

注意事项总结

  • C# 嵌套类不会自动持有外部类引用,和 Java 不同
  • 嵌套类只能访问外部类的静态成员(不传引用的前提下)
  • 要访问外部类的实例成员,必须显式传入外部类实例
  • 嵌套层数一般不超过 2 层
  • 所有五种访问修饰符都可以用于嵌套类

12. 综合示例

下面是一个完整的配置管理系统示例,综合运用了本文介绍的各种技巧:

using System;
using System.Collections.Generic;

public class AppConfig
{
    // 外部类的私有字段
    private Dictionary<string, string> settings = new Dictionary<string, string>();

    // ======== 静态嵌套类:配置键名常量 ========
    public static class Keys
    {
        public const string AppName = "AppName";
        public const string Version = "Version";
        public const string MaxUsers = "MaxUsers";
        public const string EnableLogging = "EnableLogging";
    }

    // ======== 静态嵌套类:默认值 ========
    public static class Defaults
    {
        public const string AppName = "MyApplication";
        public const string Version = "1.0.0";
        public const string MaxUsers = "100";
        public const string EnableLogging = "true";
    }

    // ======== 私有嵌套类:配置校验器(对外隐藏)========
    private class Validator
    {
        public static bool ValidateMaxUsers(string value, out int result)
        {
            if (int.TryParse(value, out result) && result > 0)
                return true;
            result = 0;
            return false;
        }

        public static bool ValidateVersion(string value)
        {
            // 简单校验版本号格式 x.y.z
            return System.Text.RegularExpressions.Regex.IsMatch(
                value, @"^\d+\.\d+\.\d+$");
        }
    }

    // ======== public 嵌套类:配置读取结果 ========
    public class ReadResult
    {
        public bool Found { get; set; }
        public string Value { get; set; }
        public bool IsValid { get; set; }
        public string ErrorMessage { get; set; }

        public static ReadResult NotFound()
            => new ReadResult { Found = false };

        public static ReadResult Ok(string value)
            => new ReadResult { Found = true, Value = value, IsValid = true };

        public static ReadResult Invalid(string value, string error)
            => new ReadResult { Found = true, Value = value, IsValid = false, ErrorMessage = error };
    }

    // ======== 外部类方法 ========

    /// <summary>
    /// 设置配置项
    /// </summary>
    public void Set(string key, string value)
    {
        settings[key] = value;
    }

    /// <summary>
    /// 读取并校验配置项
    /// </summary>
    public ReadResult Get(string key)
    {
        if (!settings.TryGetValue(key, out string value))
            return ReadResult.NotFound();

        // 使用私有嵌套类做校验
        switch (key)
        {
            case Keys.MaxUsers:
                if (!Validator.ValidateMaxUsers(value, out _))
                    return ReadResult.Invalid(value, "MaxUsers 必须是正整数");
                break;
            case Keys.Version:
                if (!Validator.ValidateVersion(value))
                    return ReadResult.Invalid(value, "Version 格式必须为 x.y.z");
                break;
        }

        return ReadResult.Ok(value);
    }

    /// <summary>
    /// 加载默认配置
    /// </summary>
    public void LoadDefaults()
    {
        Set(Keys.AppName, Defaults.AppName);
        Set(Keys.Version, Defaults.Version);
        Set(Keys.MaxUsers, Defaults.MaxUsers);
        Set(Keys.EnableLogging, Defaults.EnableLogging);
        Console.WriteLine("默认配置已加载");
    }

    /// <summary>
    /// 打印所有配置
    /// </summary>
    public void PrintAll()
    {
        Console.WriteLine("\n========== 当前配置 ==========");
        foreach (var kv in settings)
        {
            Console.WriteLine($"  {kv.Key} = {kv.Value}");
        }
        Console.WriteLine("===============================\n");
    }
}

// ========== 测试代码 ==========
class Program
{
    static void Main()
    {
        var config = new AppConfig();

        // 加载默认配置
        config.LoadDefaults();
        config.PrintAll();

        // 修改部分配置
        config.Set(AppConfig.Keys.AppName, "无敌学习系统");
        config.Set(AppConfig.Keys.MaxUsers, "500");

        // 读取并校验
        CheckConfig(config, AppConfig.Keys.AppName);
        CheckConfig(config, AppConfig.Keys.MaxUsers);
        CheckConfig(config, AppConfig.Keys.Version);

        // 故意设置一个非法的值来测试校验
        config.Set(AppConfig.Keys.Version, "不合法版本");
        CheckConfig(config, AppConfig.Keys.Version);

        // 测试不存在的配置项
        CheckConfig(config, "不存在的键名");
    }

    static void CheckConfig(AppConfig config, string key)
    {
        AppConfig.ReadResult result = config.Get(key);

        if (!result.Found)
        {
            Console.WriteLine($"[{key}] 未找到");
        }
        else if (!result.IsValid)
        {
            Console.WriteLine($"[{key}] = {result.Value} (❌ 不合法:{result.ErrorMessage})");
        }
        else
        {
            Console.WriteLine($"[{key}] = {result.Value} (✅)");
        }
    }
}

运行结果

默认配置已加载

========== 当前配置 ==========
  AppName = MyApplication
  Version = 1.0.0
  MaxUsers = 100
  EnableLogging = true
===============================

[AppName] = 无敌学习系统 (✅)
[MaxUsers] = 500 (✅)
[Version] = 1.0.0 (✅)
[Version] = 不合法版本 (❌ 不合法:Version 格式必须为 x.y.z)
[不存在的键名] 未找到

小结

要点 说明
什么是嵌套类 定义在另一个类内部的类,全名为 外部类.嵌套类
为什么用嵌套类 封装内部实现细节、避免命名冲突、逻辑分组
private 嵌套类(推荐首选) 仅外部类内部使用,对外隐藏实现
public 嵌套类 外部代码通过 Outer.Inner 方式使用
访问规则 嵌套类可访问外部类私有成员(需外部类实例);外部类也可访问嵌套类私有成员
C# vs Java C# 嵌套类不会自动持有外部类引用,需显式传入
静态嵌套类 用于常量分组、工具方法分组,用 static class 声明
嵌套层数 建议不超过 2 层,过深难维护

一句话总结:嵌套类就是把"只给某个类打工"的辅助类藏在该类里面,让代码更整洁、更安全。

0

评论区