HOWTO · Csharp
C# 中 typedef 的等效写法
使用 using 别名实现 C# 中的 typedef 等效写法,了解 C# 12 的元组别名和普通泛型类型别名。
本页内容
C# 没有 typedef 关键字。最接近的功能是 using 别名,它可以为已有的命名空间或类型指定文件范围内使用的名称。
别名可以提高可读性,但不会在运行时创建新类型。如果需要具有独立行为或验证逻辑的类型,应定义类、record 或结构体。
为现有类型使用 using 别名
将别名放在文件顶部、其他 using 指令旁边,并使用完全限定的目标类型。这样别名不会依赖其他 using 指令。
例如,using RouteTable = System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<int>>; 为已有的封闭泛型类型指定了名称。
RouteTable 只是 Dictionary<string, List<int>> 的较短写法。它不会增加类型安全性,也不会创建新声明。普通别名只在声明它的源文件中有效,不会自动应用于整个项目。
不同形式的 using 作用不同:using System.Text; 导入命名空间,using static System.Math; 导入静态成员,而 using RouteTable = ...; 创建别名。
C# 12 的元组别名和泛型类型别名
C# 12 扩展了 using 别名,使其可以指向元组等此前无法命名的类型。命名类型和构造泛型类型的普通别名在 C# 12 之前就已支持。下面的示例同时使用了这两种形式:
using RouteTable = System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<int>>;
using Coordinate = (double Latitude, double Longitude);
RouteTable routes = new() { ["home"] = [1, 2] };
Coordinate point = (52.37, 4.90);
System.Console.WriteLine($"{routes["home"].Count}:{point.Latitude:F2}");
2:52.37
这个成功示例在隔离且禁用网络的 .NET 10.0.401 环境中完成了还原和构建。项目被复制到容器的临时存储中,使用 --ignore-failed-sources 还原后运行,源 fixture 和镜像均未改变。
如果项目使用 C# 11 或更早版本,不要假设任意类型形式的别名都能编译。请升级工具链或直接写出完整类型。命名类型的别名早于 C# 12。
别名不会做什么
别名不是类似 typedef 的独立类型。RouteTable 与完整的 Dictionary 类型在编译时可以互换。如果不希望一个值被误传为另一个值,应使用 record 或结构体等真正的领域类型。
同样,元组别名只是为元组形状提供易读名称,不会把它变成命名类。元素名称有助于访问和文档说明,但不会提供独立的类型身份。
可空引用别名目标的边界
扩展后的别名语法仍有一些限制。顶层可空引用类型不能作为别名目标,因此 using MaybeName = string?; 是有意设置的无效声明。
编译器会拒绝该声明,因为 string? 是可空引用注释,而不是此位置允许的别名目标。边界 fixture 应该编译失败;它是诊断证据,不是可运行的解决方案。
何时定义真正的类型
当名称需要表示独立概念时,应选择类、record 或结构体。需要不变量、验证、方法、受控构造、相等语义或公共 API 时,真正的类型更合适。只想缩短文件内的长类型写法时,使用别名即可。
请参阅 Microsoft 关于 using 指令、C# 12 和 using 别名提案的文档。
常见问题
- C# 支持
typedef吗? 不支持。请使用using别名或真正的类型。 - 别名可以指向元组吗? 可以,C# 12 及更高版本支持。
- 别名在每个文件中都有效吗? 不有效,普通别名只在声明它的文件中有效。
- 别名会创建新类型吗? 不会,它引用已有类型。
- 为什么
using MaybeName = string?;会失败? 顶层可空引用类型被禁止作为别名目标。