小窍门
开发软件的新手? 首先开始 学习入门 教程。 它们会在编写第一个程序时引入命名空间和 using 指令。
是否在其他语言中有经验? C# 中的命名空间的工作方式类似于 Java 中的包或 Python 中的模块。 提前了解所需的语法。
命名空间声明和 using 指令是相关的语言功能。 命名空间声明将类型置于有组织的结构中。 命名空间将相关类型组合在一起,并防止命名冲突。 指令 using 允许程序按其简单名称使用这些类型。 无需在每次使用时拼写完整的命名空间路径。
你已在编写的每个 C# 程序中使用了命名空间。 每个 .NET 类型都属于一个命名空间,文件顶部的每个 using 指令都引用一个。 例如, Console 并 Math 属于 System 命名空间,因此它们的完全限定名称是 System.Console 和 System.Math。 集合类型,如 List<T> 和 Dictionary<TKey, TValue> 属于 System.Collections.Generic。 这些命名空间中的任何一 using 个指令都允许你按其简单名称引用其所有类型。 你在任何地方使用时,编写 List<T> 而不是 System.Collections.Generic.List<T>。
本文提供了有关命名空间和 using 指令工作原理的更多背景,并演示了在 .NET 库中遇到的模式示例。
命名空间包含类型。 每个 .NET 类型都属于命名空间。 例如,请考虑 System.Threading.Tasks.Task:类型 Task 属于 System.Threading.Tasks 命名空间。
最好在同一命名空间中对相关类型或类似类型进行分组,这是 .NET 对它提供的类型执行的操作。 命名空间 System.Collections.Generic 包含与集合相关的类型, System.IO 命名空间包含与读取和写入文件、目录和数据相关的类型。 命名空间 System 包含基本类型,例如 Math, DateTime和 Console。
以下示例演示命名空间如何与 using 典型 C# 文件中的指令协同工作:
using System.Globalization;
namespace MyApp.Services;
class Greeter
{
public string Greet(string name)
{
var culture = CultureInfo.CurrentCulture;
return $"Hello, {name}! Culture: {culture.Name}";
}
}
在前面的示例中,using 指令意味着您可以通过名称 CultureInfo 来使用 System.Globalization.CultureInfo,而无需指定 System.Globalization.CultureInfo 的完整名称。 该 namespace 指令声明该 Greeter 类是命名空间的 MyApp.Services 一部分。 其完全限定名称为 MyApp.Services.Greeter.
命名空间声明
命名空间声明将类型分配给命名组。 写入的每个类型都应属于命名空间。 命名空间名称通常镜像项目的文件夹结构。 例如,文件夹中的类型 Services/Payments 通常属于 MyApp.Services.Payments 命名空间。
命名空间使用 . 运算符来表示层次结构,例如 System.Collections.Generic。 命名空间名称必须是有效的 C# 标识符名称。
文件范围的命名空间
当文件中的所有类型都属于同一命名空间时,请使用 文件范围的 语法。 在命名空间声明后添加分号,并应用于整个文件。 不需要额外的大括号或缩进:
namespace MyApp.Models;
class Customer
{
public required string Name { get; init; }
public string? Email { get; init; }
public override string ToString() => $"{Name} ({Email ?? "no email"})";
}
文件范围的命名空间可减少嵌套,并使文件更易于读取。 每个文件只能有一个文件范围的命名空间声明。
小窍门
在新代码中使用文件范围的命名空间。 大多数 .NET 模板和代码分析器都建议使用此样式。
块范围的命名空间
需要在同一文件中声明多个命名空间时,请使用 块范围的 语法。 此样式增加了额外的缩进级别。
重要
在同一文件中声明多个命名空间是错误的做法。 更常见的方案是使用 文件范围的 命名空间。
以下代码片段是 块范围的 命名空间的示例:
namespace MyApp.Models
{
class Product
{
public required string Name { get; init; }
public decimal Price { get; init; }
public override string ToString() => $"{Name}: {Price:C}";
}
}
使用指令
using如果没有指令,则必须按其完全限定名称引用每个类型,完整的命名空间路径以及类型名称:
static void ShowFullyQualified()
{
// Without a using directive, use the fully qualified name:
System.Console.WriteLine("Hello from fully qualified name!");
}
using文件顶部的指令导入命名空间,以便按其简单名称使用其类型:
static void ShowShortName()
{
// With 'using System;' (or implicit usings enabled), use the short name:
Console.WriteLine("Hello from short name!");
}
有关详细信息,请参阅 using 指令。
全局 using 指令
如果在每个文件中编写相同的 using 指令,使用 全局 using 指令可以让您为整个项目统一声明一次这些指令。 将它们置于任何文件中。 许多团队创建专用 GlobalUsings.cs 文件:
global using System.Text;
global using System.Text.Json;
声明全局使用后,项目中的每个文件都可以通过使用简单名称来引用该命名空间中的类型,而无需附加 using 指令。
隐式使用指令
根据项目类型,.NET SDK 自动生成最常见命名空间的全局 using 指令。 通过在项目文件中设置 <ImplicitUsings>enable</ImplicitUsings> 来启用隐式使用。 例如,控制台应用项目会自动导入System、System.Collections.Generic、System.IO、System.Linq、System.Threading和System.Threading.Tasks。 当前 SDK 支持在使用dotnet new时创建新项目ImplicitUsings。
有关详细信息,请参阅 隐式 using 指令。
静态导入指令
指令 static using 导入类型的静态成员,以便无需类型名称前缀即可调用它们:
using static System.Math;
namespace MyApp.Utilities;
class CircleCalculator
{
public static double CalculateArea(double radius) => PI * Pow(radius, 2);
public static double CalculateCircumference(double radius) => 2 * PI * radius;
}
静态用法适用于类似 Math 且 Console 经常调用的实用工具类。
类型和命名空间别名
using别名用于为类型或命名空间创建简化名称。 别名适用于长泛型类型、解决命名冲突和提高可读性:
using CustomerList = System.Collections.Generic.List<MyApp.Models.Customer>;
namespace MyApp.Services;
class CustomerService
{
public CustomerList GetTopCustomers()
{
CustomerList customers = [new() { Name = "Alice" }, new() { Name = "Bob" }];
return customers;
}
}
从 C# 12 版本开始,你可以为任何类型创建别名,包括元组和指针类型:
using Point = (double X, double Y);
namespace MyApp.Geometry;
class Shape
{
public static double Distance(Point a, Point b)
{
var dx = a.X - b.X;
var dy = a.Y - b.Y;
return Math.Sqrt(dx * dx + dy * dy);
}
}
对于两个程序集定义相同的完全限定类型名称的更高级方案,请使用 外部别名 来消除它们之间的歧义。