using 指令

using 指令允许使用在命名空间中定义的类型,而无需指定该类型的完全限定命名空间。 using 指令以基本形式从单个命名空间导入所有类型,如下例所示:

using System.Text;

可以将两个修饰符应用于 using 指令:

  • global 修饰符与将同一 using 指令添加到项目中的每个源文件具有相同的效果。 此修饰符是在 C# 10 中引入的。
  • static 修饰符从单个类型中导入该 static 成员和嵌套类型,而不是导入命名空间中的所有类型。

可以合并这两个修饰符,将静态成员从类型导入到项目中的所有源文件。

还可以通过 using 别名指令为名称空间或类型创建别名。

using Project = PC.MyCompany.Project;

可对 using 别名指令使用 global 修饰符。

注意

using 关键字还用于创建 using 语句,此类语句有助于确保正确处理 IDisposable 对象(如文件和字体)。 有关 using 语句的详细信息,请参阅 using 语句

没有 global 修饰符的 using 指令的作用域是显示它的文件。

global using 指令必须出现在所有命名空间和类型声明之前。 所有全局 using 指令都必须出现在源文件中,然后才能显示任何非全局 using 指令。

其他 using 指令可以显示:

  • 源代码文件的开头,位于任何命名空间或类型声明之前。
  • 在任何受阻止范围的命名空间中,但在该命名空间中声明的任何命名空间或类型之前。

否则,将生成编译器错误。

创建 using 指令,以便在命名空间中使用类型而不必指定命名空间。 using 指令不为你提供对嵌套在指定命名空间中的任何命名空间的访问权限。 命名空间分为两类:用户定义的命名空间和系统定义的命名空间。 用户定义的命名空间是在代码中定义的命名空间。 有关系统定义的命名空间的列表,请参阅 .NET API 浏览器

修饰 global

using 指令添加 global 修饰符意味着 using 将应用于编译中的所有文件(通常是一个项目)。 global using 指令被添加到 C# 10 中。 其语法为:

global using <fully-qualified-namespace>;

其中 ,完全限定命名空间 是可以引用其类型而不指定命名空间的命名空间的完全限定名称。

global using 指令可以出现在任何源代码文件的开头。 单个文件中的所有 global using 指令必须出现在以下内容之前:

  • 没有 using 修饰符的所有 global 指令。
  • 文件中的所有命名空间和类型声明。

可以将指令添加到 global using 任何源文件。 通常,你想要将它们保存在单个位置。 global using 指令的顺序并不重要,无论是在单个文件中,还是在文件之间。

修饰 global 符可以与 static 修饰符组合。 global修饰符可以应用于 using alias 指令。 在这两种情况下,指令的作用域都是当前编译中的所有文件。 以下示例允许在项目中的所有文件中使用 System.Math 中声明的所有方法:

global using static System.Math;

还可以通过将 <Using> 项添加到项目文件(例如 <Using Include="My.Awesome.Namespace" />)来全局包含命名空间。 有关详细信息,请参阅 <Using>

如果在不同位置复制 global using 指令,分析器会发出诊断。 如果为已引用 using 指令的命名空间或类型global添加using指令,这些分析器也会通知你。 你可能会发现,通过将它们保存在项目中的一个文件中,可以更轻松地管理 global 你的使用。

重要

适用于 .NET 6 的 C# 模板使用顶级语句。 如果你已升级到 .NET 6,则应用程序可能与本文中的代码不匹配。 有关详细信息,请参阅有关新 C# 模板生成顶级语句的文章

.NET 6 SDK 还为使用以下 SDK 的项目添加了一组隐式 global using 指令

  • Microsoft.NET.Sdk
  • Microsoft.NET.Sdk.Web
  • Microsoft.NET.Sdk.Worker

这些隐式 global using 指令包含项目类型最常见的命名空间。

有关详细信息,请参阅有关隐式 using 指令的文章

修饰 static

using static 指令命名了一种类型,无需指定类型名称即可访问其静态成员和嵌套类型。 其语法为:

using static <fully-qualified-type-name>;

<fully-qualified-type-name> 是无需指定类型名称即可访问其静态成员和嵌套类型的类型名称。 如果不提供完全限定的类型名称(完整的命名空间名称以及类型名称),则 C# 将生成编译器错误 CS0246:“无法找到类型或命名空间名称‘type/namespace’(是否缺少 using 指令或程序集引用?)”。

using static 指令适用于任何具有静态成员(或嵌套类型)的类型,即使该类型还具有实例成员。 但是,只能通过类型实例来调用实例成员。

你可以访问类型的静态成员,而无需限定使用类型名称进行访问:

using static System.Console;
using static System.Math;
class Program
{
    static void Main()
    {
        WriteLine(Sqrt(3*3 + 4*4));
    }
}

通常,调用某个静态成员时,即会提供类型名称以及成员名称。 重复输入相同的类型名称来调用该类型的成员将生成详细的晦涩代码。 例如,Circle 类的以下定义引用 Math 类的许多成员。

using System;

public class Circle
{
   public Circle(double radius)
   {
      Radius = radius;
   }

   public double Radius { get; set; }

   public double Diameter
   {
      get { return 2 * Radius; }
   }

   public double Circumference
   {
      get { return 2 * Radius * Math.PI; }
   }

   public double Area
   {
      get { return Math.PI * Math.Pow(Radius, 2); }
   }
}

通过消除每次引用成员时,显式引用 Math 类的需求,using static 指令将生成更简洁的代码:

using System;
using static System.Math;

public class Circle
{
   public Circle(double radius)
   {
      Radius = radius;
   }

   public double Radius { get; set; }

   public double Diameter
   {
      get { return 2 * Radius; }
   }

   public double Circumference
   {
      get { return 2 * Radius * PI; }
   }

   public double Area
   {
      get { return PI * Pow(Radius, 2); }
   }
}

using static 仅导入可访问的静态成员和指定类型中声明的嵌套类型。 不导入继承的成员。 可以从任何带 using static 指令的已命名类型导入,包括 Visual Basic 模块。 如果 F# 顶级函数在元数据中显示为一个已命名类型(其名称是有效的 C# 标识符)的静态成员,则可以导入该 F# 函数。

using static 使指定类型中声明的扩展方法可用于扩展方法查找。 但是,扩展方法的名称不导入到代码中非限定引用的作用域中。

同一编译单元或命名空间中通过不同 using static 命令从不同类型导入的具有相同名称的方法组成一个方法组。 这些方法组内的重载解决方法遵循一般 C# 规则。

以下示例使用 using static 指令来提供 ConsoleMathString 类的静态成员,而无需指定其类型名称。

using System;
using static System.Console;
using static System.Math;
using static System.String;

class Program
{
   static void Main()
   {
      Write("Enter a circle's radius: ");
      var input = ReadLine();
      if (!IsNullOrEmpty(input) && double.TryParse(input, out var radius)) {
         var c = new Circle(radius);

         string s = "\nInformation about the circle:\n";
         s = s + Format("   Radius: {0:N2}\n", c.Radius);
         s = s + Format("   Diameter: {0:N2}\n", c.Diameter);
         s = s + Format("   Circumference: {0:N2}\n", c.Circumference);
         s = s + Format("   Area: {0:N2}\n", c.Area);
         WriteLine(s);
      }
      else {
         WriteLine("Invalid input...");
      }
   }
}

public class Circle
{
   public Circle(double radius)
   {
      Radius = radius;
   }

   public double Radius { get; set; }

   public double Diameter
   {
      get { return 2 * Radius; }
   }

   public double Circumference
   {
      get { return 2 * Radius * PI; }
   }

   public double Area
   {
      get { return PI * Pow(Radius, 2); }
   }
}
// The example displays the following output:
//       Enter a circle's radius: 12.45
//
//       Information about the circle:
//          Radius: 12.45
//          Diameter: 24.90
//          Circumference: 78.23
//          Area: 486.95

在此示例中, using static 该指令也可以应用于 Double 该类型。 添加该指令使得在未指定类型名称情况下调用 TryParse(String, Double) 方法成为可能。 但是,使用没有类型名称的 TryParse 创建的代码可读性较差,因为有必要检查 using static 指令,以确定所调用的数值类型的 TryParse 方法。

using static 也适用于 enum 类型。 通过在枚举中添加 using static,该类型不再需要使用枚举成员。

using static Color;

enum Color
{
    Red,
    Green,
    Blue
}

class Program
{
    public static void Main()
    {
        Color color = Green;
    }
}

别名using

创建 using 别名指令,以便更易于将标识符限定为命名空间或类型。 在任何 using 指令中,都必须使用完全限定的命名空间或类型,而无需考虑它之前的 using 指令。 using 指令的声明中不能使用 using 别名。 例如,以下示例生成一个编译器错误:

using s = System.Text;
using s.RegularExpressions; // Generates a compiler error.

下面的示例显示如何为命名空间定义和使用 using 别名:

namespace PC
{
    // Define an alias for the nested namespace.
    using Project = PC.MyCompany.Project;
    class A
    {
        void M()
        {
            // Use the alias
            var mc = new Project.MyClass();
        }
    }
    namespace MyCompany
    {
        namespace Project
        {
            public class MyClass { }
        }
    }
}

using 别名指令的右侧不能有开放式泛型类型。 例如,不能为 List<T> 创建 using 别名,但可以为 List<int> 创建 using 别名。

下面的示例显示如何为类定义 using 指令和 using 别名:

using System;

// Using alias directive for a class.
using AliasToMyClass = NameSpace1.MyClass;

// Using alias directive for a generic class.
using UsingAlias = NameSpace2.MyClass<int>;

namespace NameSpace1
{
    public class MyClass
    {
        public override string ToString()
        {
            return "You are in NameSpace1.MyClass.";
        }
    }
}

namespace NameSpace2
{
    class MyClass<T>
    {
        public override string ToString()
        {
            return "You are in NameSpace2.MyClass.";
        }
    }
}

namespace NameSpace3
{
    class MainClass
    {
        static void Main()
        {
            var instance1 = new AliasToMyClass();
            Console.WriteLine(instance1);

            var instance2 = new UsingAlias();
            Console.WriteLine(instance2);
        }
    }
}
// Output:
//    You are in NameSpace1.MyClass.
//    You are in NameSpace2.MyClass.

从 C# 12 开始,可为以前受限的类型(包括元组类型、指针类型和其他不安全类型)创建别名。 有关更新的规则的详细信息,请参阅功能规范

限定的别名成员

命名空间别名限定符提供 :: 对全局命名空间或其他可能由其他实体隐藏的别名的显式访问。

确保 global:: 令牌后面的 :: 命名空间查找相对于全局命名空间。 否则,令牌必须解析为使用别名,并且后面的 :: 令牌必须解析为该别名命名空间中的类型。 以下示例显示了这两种形式:

using S = System.Net.Sockets;

class A
{
    public static int x;
}

class C
{
    public void F(int A, object S)
    {
        // Use global::A.x instead of A.x
        global::A.x += A;

        // Using ::, S must resolve to a namespace alias:
        S::Socket s = S as S::Socket;

        // In this form, if S were a class, it would be a compile-time error:
        S.Socket s1 = S as S.Socket;
    }
}

C# 语言规范

有关详细信息,请参阅 C# 语言规范中的 Using 指令。 该语言规范是 C# 语法和用法的权威资料。

有关全局 using 修饰符的详细信息,请参阅全局 using 功能规范 - C# 10

另请参阅