嵌套类型
本教程共 100 篇 · 第 39 篇 · 更新于 2026-07-31 · 约 11 分钟阅读
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 面。实例化嵌套类要写全名,且它不会自动绑定外层实例。能复用就别嵌套,只为某一个类服务才嵌套。