C#中的using用法总结

  using一般有两个作用:

  1、作为语句,用于定义一个范围,在此范围的末尾将释放对象(IDisposable和IAsyncDisposable接口)

  2、作为指令,用于引入命名空间或者类型,或者为引入的命名空间或者类型定义别名

  using语句

  using语句应该都很熟悉了吧,从最早的ADO.net,或者对文件、流的操作,一般都会使用using语句吧。

  using(await using)语句的作用对象是实现了IDisposable(IAsyncDisposable)接口的对象,而实现IDisposable或者IAsyncDisposable接口的对象一般都是一些非托管对象,比如文件,我们在操作完非托管对象后,一般需要释放它们,而using(await using)就类似一个语法糖,它为IDisposable(IAsyncDisposable)接口的对象的提供了一个作用范围,在退出这个作用返回的时候,会自动调用Dispose(DisposeAsync)方法。

  IAsyncDisposable是IDisposable的异步形式,一个简单的使用例子:

    public static async Task Main(string[] args)
    {
        //using针对IDisposable接口对象
        using (var reader = new StreamReader("text.log"))
        {
            var text = reader.ReadToEnd();
        }
        //await using针对IAsyncDisposable接口对象
        await using (var myUnmanagedObject = new MyUnmanagedObject())
        {
            var str = myUnmanagedObject.ToString();
        }
    }
    class MyUnmanagedObject : IAsyncDisposable
    {
        public async ValueTask DisposeAsync()
        {
            await Task.CompletedTask;
            Console.WriteLine("DisposeAsync");
        }
    }

  其实using(await using)的作用等价于下面的形式:

    public static async Task Main(string[] args)
    {
        //using针对IDisposable接口对象
        var reader = new StreamReader("text.log");
        try
        {
            var text = reader.ReadToEnd();
        }
        finally
        {
            reader.Dispose();
        }
        //await using针对IAsyncDisposable接口对象
        var myUnmanagedObject = new MyUnmanagedObject();
        try
        {
            var str = myUnmanagedObject.ToString();
        }
        finally
        {
            await myUnmanagedObject.DisposeAsync();
        }
    }

  也就是说,在using(await using)的范围内,无论有没有发生错误,都会自动调用Dispose(DisposeAsync)方法。

  为了更方便的使用using(await using),我们还可以省略大括号,这意味着,using(await using)的作用区间就是从using(await using)开始至变量退出的区间,可以简单的理解为可以使用这个变量的范围区间,比如:

    public static async Task Main(string[] args)
    {
        //using针对IDisposable接口对象
        using var reader = new StreamReader("text.log");
        var text = reader.ReadToEnd();

        //await using针对IAsyncDisposable接口对象
        await using var myUnmanagedObject = new MyUnmanagedObject();
        var str = myUnmanagedObject.ToString();

        //退出方法前会自动调用Dispose或者DisposeAsync方法
        //也就是说using的范围是using开始至方法结尾
    }

   using指令

   using指令常用于引入命名空间或者类,目前(C#10)一般有4种格式:

  1、引入命名空间:using [命名空间]

  比如:  

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    namespace ClassLibrary
    {
        using System.IO;
        using System.Drawing;

        //其它代码
    }
    using的位置
    1、源代码文件的开头,位于任何命名空间或类型声明之前。
    2、如果在任何命名空间中,则必须在该命名空间中的所有命名空间和类型之前

  2、与global结合(C#10提供):global using [命名空间]

  比如:  

    global using System;
    global using System.Collections.Generic;
    global using System.Linq;
    global using System.Text;
    global using System.Threading.Tasks;

   global using和using的区别是,using只是将命名空间作用于当前的源文件,而global using就是将命名空间作用于当前项目的所有源文件,这样就不需要我们每个文件都去引入了。

  这是很好用的一个语法糖,我们可以在任何一个C#的源文件中使用global using,但是global using必须出现在文件开头,包括在那些没有global的using开头!

  :global using的作用,我们也可以通过修改csproj文件还完成:  

    <ItemGroup>
        <Using Include="System"/>
        <Using Include="System.Collections.Generic"/>
        <Using Include="System.Linq"/>
        <Using Include="System.Text"/>
        <Using Include="System.Threading.Tasks"/>
    </ItemGroup>

  3、与static结合:using static [全限定名称的类型]

  using static的作用就是将指定的类型的静态成员展开,这样我们在用的时候就不需要再写类型了,而是直接使用方法就可以了:  

    using static System.Console;

    namespace ClassLibrary
    {
        class Program
        {
            void Main(string[] args)
            {
                WriteLine("Hello World!");

                //等价于

                Console.WriteLine("Hello World!");
            }
        }
    }

   使用using static时,需要注意几点:  

    1、using static可以作用于任何类型,包括结构体、嵌套类型等,但是使用时必须使用全限定名称(命名空间+类型名成)
    2、当using static导致方法名称冲突时,需要使用通常的类型来指明,比如类中已经定义了一个WriteLine方法,那么就需要显示的用类型来指明
    3、using static只会导入自定义的静态成员,不会导入父类的静态成员

  此外,static也可以与global一起使用,表示往项目中的每个源文件都导入指定类型的静态成员,比如:  

    global using static System.Console;

    namespace ClassLibrary
    {
        class Program
        {
            void Main(string[] args)
            {
                WriteLine("Hello World!");

                //现在你可以在当前项目的其它原文件中使用Console中的静态成员了
            }
        }
    }

  :同样,我们可以通过修改csproj项目文件来实现global using static 的相关:  

    <ItemGroup>
        <Using Include="System.Console" Static="true"/>
    </ItemGroup>

  4、定义别名:using [别名]=[命名空间|类型]

  别名用于简化我们的代码,using定义的别名可以作用于命名空间,或者类型,比如:  

    namespace ClassLibrary
    {
        using s = System;
        using c = System.Console;

        class Program
        {
            void Main(string[] args)
            {
                var now = s.DateTime.Now;
                c.WriteLine("Hello World!");
            }
        }
    }

  需要注意的是,这里的using,无论是对命名空间,还是类型,都必须使用全限定名称!

  当然,using别名也可以与global结合,这里就不重复叙述了。

  

  结语

  说到using引入命名空间,不知道大家有没有考虑过这么一个问题,假如有两个dll,里面都有一个相同名称,相同命名空间的类型,如果我们同一个项目中引用这两个dll,我们怎么指定使用哪个类型?

  例如:LibraryA项目有个类Library.MyClass,LibraryB项目有个类Library.MyClass,如果我们有一个LibraryC项目同时引用了LibraryA和LibraryB,那么在LibraryC中要使用LibraryA的Library.MyClass,如果直接引用Library.MyClass,那么肯定会报错,因为程序不知道到底是LibraryA还是LibraryB。这个时候,我们需要定义一个命名来区分。

  在我们引用项目或者dll文件的依赖项中,右键依赖项=》属性,开发属性面板,有个别名的栏位,我们可以将LibraryA和LibraryB定义两个不一样的别名:

  

  此外,我们也可以通过修改csproj项目文件来添加别名:  

  <ItemGroup>
    <ProjectReference Include="..\LibraryA\LibraryA.csproj">
      <Aliases>A</Aliases>
    </ProjectReference>
    <ProjectReference Include="..\LibraryB\LibraryB.csproj">
      <Aliases>B</Aliases>
    </ProjectReference>
  </ItemGroup>

  接着在代码中使用extern alias声明一个外面的别名,就可以正常使用了:  

    extern alias A;
    extern alias B;

    namespace ClassLibrary
    {
        class Program
        {
            void Main(string[] args)
            {
                var ClassA = new A::Library.MyClass();
                var ClassB = new B::Library.MyClass();
            }
        }
    }

  此时,using别名就提供了一个很好的作用:  

    extern alias A;
    extern alias B;

    global using LibraryA = A::Library;
    global using LibraryB = B::Library;

    namespace ClassLibrary
    {
        class Program
        {
            void Main(string[] args)
            {
                var ClassA = new LibraryA.MyClass();
                var ClassB = new LibraryB.MyClass();
            }
        }
    }

  剩下的工作就交给LibraryA、LibraryB...

 

  参考文档:

  https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/using

  https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-directive

  

posted @ 2023-06-29 13:34  没有星星的夏季  阅读(2130)  评论(0编辑  收藏  举报