向 using 指令添加 global 修饰符意味着 using 将应用于编译中的所有文件(通常是一个项目)。 global using 指令被添加到 C# 10 中。 其语法为:
global using <fully-qualified-namespace>;
其中 fully-qualified-namespace 是命名空间的完全限定名称,其类型可以在不指定命名空间的情况下引用。
global using 指令可以出现在任何源代码文件的开头。 单个文件中的所有 global using 指令必须出现在以下内容之前:
可以将 global using 指令添加到任何源文件中。 通常,你需要将它们保留在一个位置。 global using 指令的顺序并不重要,无论是在单个文件中,还是在文件之间。
global 修饰符可以与 static 修饰符合并。 global 修饰符可以应用于 using 别名指令。 在这两种情况下,指令的作用域都是当前编译中的所有文件。 以下示例允许在项目中的所有文件中使用 System.Math 中声明的所有方法:
global using static System.Math;
还可以通过将
项添加到项目文件(例如
)来全局包含命名空间。
创建 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
创建 using 别名,但可以为 List
创建 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 dt = System.DateTime;
使用别名要小心,如果别名是一个已使用的名称,则容易造成错误:
global using DateTime = System.Text.Encoding;
但是,反过来想:如果上面的 Encoding 包含了 Now 的定义,是不是就可以替换掉现有的 DateTime.Now 实现呢?
新建一个 .cs 文件,在顶部使用 global using 设置 DateTime 为别名:
global using DateTime = ConsoleApp1.MyIODateTime;
在 MyIODateTime 中同样实现 Now 属性:
public static class MyIODateTime
{
public static System.DateTime Now
{
get
{
Console.WriteLine("Call from MyIODateTime");
return new System.DateTime(2022, 1, 1);
}
}
}
在 ConsoleApp1 中执行 Console.WriteLine(DateTime.Now);,可以发现 DateTime.Now 确实被替换了:
using static 指令命名了一种类型,无需指定类型名称即可访问其静态成员和嵌套类型。 其语法为:
using static <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 指令来提供 Console、Math 和 String 类的静态成员,而无需指定其类型名称。
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 指令
global using 的另类用法