首页 / C# 入门教程 / 嵌套类型

C# 入门教程

嵌套类型

本教程共 100 篇 · 第 39 篇 · 更新于 2026-07-31 · 约 11 分钟阅读

C#C# 入门教程嵌套类型nested type面向对象

39. 嵌套类型

本节目标:学完你能在一个类型内部定义另一个类型,并知道何时该这么做。

有些小类型只服务于某一个大类,单独建文件反而散。C# 允许你在类或结构内部再定义类型,这叫嵌套类型(nested type),可以是类、枚举、接口、结构等。

最基础的嵌套:内部枚举

最常见的是把「只和该类相关的枚举」嵌进去,避免污染外层命名空间。下面 BankAccount 内嵌一个 AccountType 枚举。

namespace CSharpDemo;

class BankAccount
{
    public enum AccountType
    {
        Savings,
        Checking
    }

    public AccountType Type { get; set; }
    public decimal Balance { get; set; }
}

var acc = new BankAccount
{
    Type = BankAccount.AccountType.Savings,
    Balance = 1000m
};
Console.WriteLine($"类型:{acc.Type},余额:{acc.Balance}");

外部访问时用 BankAccount.AccountType 这种「外层.内层」的路径,归属关系一目了然。

嵌套类:访问外层私有成员

嵌套类(nested class)有个独门优势:它能直接访问外层类型的私有成员。这是普通平级类做不到的。

namespace CSharpDemo;

class BankAccount
{
    private decimal limit = 5000m;   // 外层私有字段

    public class Transaction          // 嵌套类
    {
        public string Note { get; set; }
        public void PrintLimit(BankAccount acc)
        {
            Console.WriteLine($"该账户额度:{acc.limit}");  // 能碰私有
        }
    }
}

var acc = new BankAccount();
var t = new BankAccount.Transaction { Note = "存款" };
t.PrintLimit(acc);   // 该账户额度:5000
Note

注意实例化嵌套类时也要写全名 new BankAccount.Transaction();嵌套类并不自动绑定某个外层实例。

访问可见性规则

嵌套类型自己的访问修饰符,相对于「外层类型之外」的世界生效,默认是 private——也就是只有外层类型内部能用。

修饰符含义
private(默认)仅外层类型内部可见
public谁都能用,用 外层.内层 访问
internal同程序集可见
protected外层及其派生类可见
namespace CSharpDemo;

class Outer
{
    private class Secret { }        // 只有 Outer 内部能碰
    public class Visible { }        // 外部可访问
}

// var s = new Outer.Secret();   // 报错:private
var v = new Outer.Visible();        // 正常
Tip

新手常以为嵌套类型默认 internal,其实它默认是 private。想对外暴露一定要显式写 public

典型使用场景

  • 专属状态/选项:像上面的 AccountType,只配合 BankAccount 才有意义。
  • 内部辅助结构:比如 TreeNode 只服务于某个树结构类,没必要暴露给全局。
  • 隐藏实现细节:把对外不该看到的类型嵌套并设为 private,缩减公开 API 面。
  • 需要访问外层私有数据:用嵌套类最自然,避免把字段改成 public
Note

别滥用嵌套。如果一个类型在多处被复用,就该提升为顶层类型;只有「明显只属于某一个类」时才嵌套。

和静态的关系

嵌套类可以是 static,也可以不是。实例嵌套类要访问外层实例成员时,需要像前面那样把外层对象传进去;若外层成员是 static,嵌套类直接访问即可,无需传对象。

不只嵌套类

类内部还能嵌套接口、结构、甚至另一个枚举,规则一致。比如把一个只在内部使用的比较器接口嵌进去,外部完全看不见。

namespace CSharpDemo;

class Report
{
    public interface IFormatter
    {
        string Format(string text);
    }

    private class Plain : IFormatter
    {
        public string Format(string t) => t;
    }
}

嵌套类型访问静态外层成员

前面看到嵌套类能读外层实例的私有字段;若外层成员是 static,嵌套类无需任何外层对象就能直接访问,因为它属于类型层面。

namespace CSharpDemo;

class Outer
{
    private static int seed = 100;
    public class Inner
    {
        public int Next() => seed++;   // 直接碰静态私有
    }
}

Console.WriteLine(new Outer.Inner().Next());   // 100

多层嵌套与泛型

嵌套可以多层,访问路径也跟着加长:A.B.C。嵌套类型也能带泛型参数,用来表达「跟外层类型绑定的容器」。

namespace CSharpDemo;

class Cache<T>
{
    public class Entry
    {
        public T? Value { get; set; }
    }
}

var e = new Cache<string>.Entry { Value = "数据" };
Console.WriteLine(e.Value);
Note

多层嵌套路径变长,可读性下降。一般最多两层,再深就该反思结构是否合理。

常见坑

  • 实例化要写全名:用的是 new Outer.Inner(),漏掉外层名会找不到类型。
  • 没有自动绑定:嵌套类不会自动持有外层实例,想访问实例成员得像前面那样把外层对象传进去。
  • 误以为能继承外层:嵌套只是「摆放位置」关系,不是继承关系,外层的方法嵌套类并不会自动拥有。

何时不该嵌套

只要这个类型在别处也可能被复用,就提升为顶层类型。嵌套只留给「明显只属于某一个类、且最好藏起来」的辅助类型。过度嵌套会让文件难以导航。

嵌套枚举做状态机

把状态枚举嵌进类里,类型归属清晰,外部也不会误用别处的定义。下面用嵌套枚举表达订单状态流转。

namespace CSharpDemo;

class Order
{
    public enum Status { Created, Paid, Shipped, Done }
    public Status State { get; set; } = Status.Created;
}

var o = new Order();
o.State = Order.Status.Paid;
Console.WriteLine(o.State);   // Paid

protected 嵌套类型

嵌套类型自身也能标 protected:只有外层类型及其派生类能用。这在「基类提供一套仅供子类使用的辅助类型」时很有用。

namespace CSharpDemo;

class Base
{
    protected class Helper
    {
        public string Tag => "辅助";
    }
}

class Derived : Base
{
    public string Use() => new Helper().Tag;   // 派生类能用
}

Console.WriteLine(new Derived().Use());   // 辅助
Note

嵌套类型即便标 public,也仍通过「外层.内层」访问,外界始终知道它从属于谁。这种强归属感是平级类型给不了的。

多层嵌套的取舍

层级越深,访问路径越长(如 A.B.C),可读性下降。经验上最多两层,再深就该反思:是不是该把内层提为顶层、只保留真正紧密相关的那一两层。

Tip

常见坑:嵌套类实例化必须写全名 new Outer.Inner();且嵌套类不会自动持有外层实例,要访问实例成员需把外层对象传进去。

访问修饰符组合的影响

嵌套类型的可见性有两层含义:一是「对外部世界是否可见」(靠 public/internal/private),二是「嵌套类型自身成员对外的可见性」(照常规则)。例如 public 嵌套类里的 private 字段,仍只有该嵌套类内部能碰。

namespace CSharpDemo;

class Outer
{
    public class Inner
    {
        private int secret;
        public int Show() => secret;   // secret 只归 Inner 管
    }
}

// new Outer.Inner().secret;   // 报错:secret 是 Inner 的 private

嵌套类型适合做「私有状态载体」

如果一个类需要一份复杂的中间状态,又不想让它暴露到全局命名空间,就把承载它的小类型嵌进来并设为 private。外部完全不知其存在,类内部却用得顺手。

Note

这种「藏起来的小类型」能显著缩小公开 API 面。公开的东西越少,别人用你的类越不容易误用,维护者也越自由地改内部实现。

小结

嵌套类型定义在一个类型的内部,常见为嵌套枚举、嵌套类,也可以是接口或结构,甚至能标 protected 仅供派生类使用。它默认 private,对外可见必须标 public,访问路径是「外层.内层」。嵌套类能直接读外层类型的私有成员(包括静态私有),且自身成员的可见性仍按常规规则,这是它最大的特长。适合放「只服务于某一个大类」的辅助类型、专属枚举和需访问私有数据的内部逻辑,或作为私有状态载体来缩小公开 API 面。实例化嵌套类要写全名,且它不会自动绑定外层实例。能复用就别嵌套,只为某一个类服务才嵌套。