using 指令

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

using System . Text;

using 指令可以使用下列两个修饰符(或者共用):

  • global 修饰符(using 之前)指定该 using 指令的内容添加到项目中的每个源文件。此修饰符是 C# 10 中引入的。
  • static 修饰符(using 之后)仅从单个类型中导入其 static 成员和嵌套类型,而不是导入命名空间中的所有类型。
  • 共用两者,即将该 using 语句导入的嵌套类型和 static 成员添加到项目中的每个源文件。

创建别名

可以使用 using 指令将一个命名空间指定一个别名,你容易记得或者理解得。可以是 global(全局)的。

using IO = System . IO;
using WJ = System . IO . FileInfo;

namespace C__的_using
{
    internal class Program
    {
        static void Main(string[] args)
        {
            FileInfo WJ1 = new ( @"C:\1234.txt" ); // 使用普通的声明方式
            WJ WJ2 = new ( @"C:\Test.txt" ); // 使用别名“WJ”声明
            Console . WriteLine ( WJ2 . FullName );
        }
    }
}

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

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

其他 using 指令可以显示:

  • 源代码文件的开头,位于任何命名空间或类型声明之前。
  • 在任何受阻止范围的命名空间中,但在该命名空间中声明的任何命名空间或类型之前。
  • 否则,将生成编译器错误。

创建 using 指令,以便在命名空间中使用类型而不必指定命名空间。using 指令不为你提供对嵌套在指定命名空间中的任何命名空间的访问权限。命名空间分为两类:用户定义的命名空间和系统定义的命名空间。用户定义的命名空间是在代码中定义的命名空间。

global 修饰符

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

global using <fully-qualified-namespace>;

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

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

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

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

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

global using static System . Math;

还可以通过将 <Using> 项添加到项目文件(例如 <Using Include = "My.Awesome.Namespace" />)来全局包含命名空间。

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

重要:适用于 .NET 6 的 C# 模板使用顶级语句。如果你已升级到 .NET 6,则应用程序可能与本文中的代码不匹配。

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

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

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

static 修饰符

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

using static < 完全限定类型名称 >;

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

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

你可以访问命名空间的 static 成员,而无需限定使用命名空间名称进行访问:

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

namespace C__的_using
{
    internal class Program
        {
            static void Main(string[] args)
                {
                    WriteLine ( Sqrt ( Pow ( 3 , 2 ) + Pow ( 4 , 2 ) ) );
                }
        }
}

通常,调用某个 static 成员时,即会提供类型名称以及成员名称。重复输入相同的类型名称来调用该类型的成员将生成详细的晦涩代码。例如,Circle 类的以下定义引用 Math 类的许多成员。通过消除每次引用成员时,显式引用 Math 类和 Console 类的需求,using static 指令将生成更简洁的代码:

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

namespace C__的_using
{
    internal class Program
        {
            static void Main(string[] args)
                {
                    Lei圆 y1 = new ( 2.5 );
                    WriteLine ( y1 );
                }
        }

    public class Lei圆
        {
            private double _半径;
            public Lei圆 ( double 半径 )
              {
                  _半径 = 半径;
              }

           public double 直径
              {
                  get => 2 * _半径;
              }

          public double 周长
              {
                  get => Tau * _半径;
              }

          public double 面积
              {
                get => Pow ( _半径 , 2 ) * PI;
              }

          public override string ToString ( )
              {
                            return $"半径:{_半径}(直径:{直径})的圆周长:{周长};面积:{面积}";
              }
        }
}

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

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

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

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

using static System . Console;
using static System . String;
using static System . Double;

namespace C__的_using
{
    internal class Program
        {
            static void Main(string[] args)
                {
                    Write ( "请输入圆的半径:" );
                    var Shr = ReadLine ( );
                    if ( !IsNullOrEmpty ( Shr ) && TryParse ( Shr , out var BJ ) )
                        {
                            Lei圆 Y = new ( BJ ) ;

                            string zfc结果 = "\n关于圆的信息:\n";
                            zfc结果 += Format ( $"半径:{BJ:N2}\n" );
                            zfc结果 += Format ( $"直径:{Y . 直径:N2}\n" );
                            zfc结果 += Format ( $"周长:{Y . 周长:N2}\n" );
                            zfc结果 += Format ( $"面积:{Y . 面积:N2}\n" );
                            WriteLine ( zfc结果 );
                        }
                    else
                        {
                            WriteLine ( "错误输入!" );
                        }
                }
    }

    public class Lei圆
        {
            private double _半径;
            public Lei圆 ( double 半径 )
                {
                    _半径 = 半径;
                }

            public double 直径
                {
                    get => 2 * _半径;
                }

            public double 周长
                {
                    get => Tau * _半径;
                }

            public double 面积
                {
                    get => Pow ( _半径 , 2 ) * Pi;
                }

            public override string ToString ( )
                {
                    return $"半径:{_半径}(直径:{直径})的圆周长:{周长};面积:{面积}";
                }
        }
}

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

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

using static YanSe;


enum YanSe
    {
        无 = 0,
        红 = 1,
        蓝 = 2,
        绿 = 4,
    }
namespace C__的_using
{
    internal class Program
    {
        static void Main(string[] args)
            {
                YanSe Lv = 绿;
            }
    }

using 别名

创建 using 别名指令,以便更易于将标识符限定为命名空间或类型。在任何 using 指令中,都必须使用完全限定的命名空间或类型,而无需考虑它之前的 using 指令。using 指令的声明中不能使用 using 别名。

namespace WodePC
    {
        using XiTong = WodePC . Cp . Win;
        class A
            {
                void FF ( )
                    {
                       var s = new XiTong . Sys ( );
                    }
            }
    namespace Cp
        {
            namespace Win
                {
                    public class Sys
                        {
                        }
                }
        }        
    }

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

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

using Lei标准 = C__的_using . KJ1 . Lei1;
using Lei泛型 = C__的_using . KJ2 . Lei2<int>;

namespace C__的_using
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var l1 = new Lei标准 ( );
            Console . WriteLine ( l1 );

            var l2 = new Lei泛型 ( );
            Console . WriteLine ( l2 );
        }
    }

    namespace KJ1
        {
        public class Lei1
            {
                public override string ToString ( )
                    {
                        return "现在是命名空间1 的类1。";
                    }
            }
    }

    namespace KJ2
        {
        public class Lei2<T>
            {
            public override string ToString ( )
                {
                    return "现在是命名空间2 的类2。";
                }
            }
        }
}

从 C# 12 开始,可为以前受限的类型(包括元组类型、指针类型和其他不安全类型)创建别名。

限定的别名成员

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

global:: 确保对 :: 令牌后面的名称空间的名称空间查找是相对于全局名称空间的。否则,令牌必须解析为 using 别名,而 :: 后面的令牌必须解析为该别名命名空间中的类型。下面的例子展示了这两种形式:

class A
    {
        public static int x;
    }

class B
    {
        public static void FF ( int A , object B )
            {
                // 使用 global::A . x 代替 A . x
                global::C__的_using . Program . A . x += A;
                // 使用 ::,S 必须解析为名称空间别名
                S::Socket? s = B as S::Socket;
                // 在这种形式下,如果 S 是一个类,它将是一个编译时错误:
                S . Socket? s1 = B as S::Socket;
            }
    }

using 语句

using 语句可确保正确使用实现 IDisposable 接口的实例:

List < int > LBZhss = new ( );
using ( StreamReader LiuDu = File . OpenText ( @".\整数\Zhss.txt" ) )
    {
        string? zfc行;
        while ( ( zfc行 = LiuDu . ReadLine ( ) ) is not null )
            {
                if ( int . TryParse ( zfc行 , out int zhs ) )
                    {
                        LBZhss . Add ( zhs );
                    }
            }
    } // 确保调用 StreamReader 的 Dispose 关闭
foreach ( int zhs in LBZhss )
    {
        Console . WriteLine ( zhs );
    }

若要上例运行正常,请在资源管理器中为该项目创建一个“整数”文件夹,并创建一个“Zhss.txt”文本文件,内容是几行字符,若某些行可以被转换为 int,则将被添加到 List < int > 中,直到文件末尾(即 zfc行 == null)。

using 可以嵌套。当控件离开 using 语句块时,将释放该语句块获取的 IDisposable 实例。using 语句可确保即使在 using 语句块内发生异常的情况下也会释放可释放实例。在前面的示例中,打开的文件在处理完所有行后关闭。

对于实现 IAsyncDisposable 接口的实例,需要调用 await using:

await using (var resource = new AsyncDisposableExample())
    {
        // Use the resource
    }

还可以使用不需要大括号的 using 声明:

static List<int> FF加载整数 ( string 路径 )
    {
        using StreamReader LiuDu = File . OpenText ( 路径 );
        List<int> Zhss = new ( );
        string? zfc行;
        while ( ( zfc行 = LiuDu . ReadLine ( ) as string ) is not null )
            {
                if ( int . TryParse ( zfc行 , out int zhs ) )
                    {
                        Zhss . Add ( zhs );
                    }
            }
        return Zhss;
    }

在 using 没有大括号时,其声明的变量将在相应作用域末尾释放。上例中的 StreamReader LiuDu 将在 FF加载整数 方法执行完毕后释放。

using 声明的变量是只读的。在其生存期,无法修改或重分配该变量;或将其作为 ref 或 out 参数传递。

一个 using 语句可以声明多个同一类型的实例:

using ( StreamReader LiuDuZhs = File . OpenText ( "Zhss.txt" ) , LiuDuWenZi = File . OpenText ( "words.txt" ) )
{
    // Process both files
} // 确保调用 StreamReader 的 Dispose 关闭 LiuDuZhs 和 LiuDuWenZi

在一个 using 语句中声明多个实例时,它们将按声明的相反顺序释放。

还可以将 using 语句和声明与适用于可释放模式的 ref 结构的实例一起使用。也就是说,它有一个实例 Dispose 方法,该方法是可访问、无参数的并且具有 void 返回类型。

using 语句也可以采用以下形式:

using ( 表达式 )
{
    // ...
}

其中 表达式 会生成可释放的实例,例如:

StreamReader LiuDu = File . OpenText ( LJ );

using ( LiuDu )
{
    // Process file content
}

警告:在前面的示例中,当控件离开 using 语句后,实例 LiuDu 在其释放后仍保留在范围内。如果进一步使用该实例,可能会遇到异常,例如 ObjectDisposedException。因此,建议在 using 语句中声明可释放变量,或者使用 using 声明进行声明。


兔子码农
4 声望1 粉丝

一个酒晕子