天天看點

C# using用法

作者:Edwin2802

using 關鍵字兩個主要用途:

  • using 語句定義一個範圍,在此範圍的末尾将釋放對象。
  • using 指令為命名空間建立别名,或導入在其他命名空間中定義的類型。

一、using語句

using 語句可確定正确使用 IDisposable 執行個體:

var numbers = new List<int>();
using (StreamReader reader = File.OpenText("numbers.txt"))
{
    string line;
    while ((line = reader.ReadLine()) is not null)
    {
        if (int.TryParse(line, out int number))
        {
            numbers.Add(number);
        }
    }
}           

當控件離開 using 語句塊時,将釋放擷取的 IDisposable 執行個體。 using 語句可確定即使在 using 語句塊内發生異常的情況下也會釋放可釋放執行個體。 在前面的示例中,打開的檔案在處理完所有行後關閉。

使用 await using 語句來正确使用 IAsyncDisposable 執行個體:

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

還可使用不需要大括号的 using 聲明:

static IEnumerable<int> LoadNumbers(string filePath)
{
    using StreamReader reader = File.OpenText(filePath);
    
    var numbers = new List<int>();
    string line;
    while ((line = reader.ReadLine()) is not null)
    {
        if (int.TryParse(line, out int number))
        {
            numbers.Add(number);
        }
    }
    return numbers;
}           

在 using 聲明中進行聲明時,局部變量在聲明它的作用域末尾釋放。 在前面的示例中,會在某個方法的末尾釋放對象。由 using 語句或聲明進行聲明的變量是隻讀的。 無法重新配置設定該變量或将其作為 ref 或 out 參數傳遞。

可在 using 語句中聲明多個相同類型的執行個體:

using (StreamReader numbersFile = File.OpenText("numbers.txt"), wordsFile = File.OpenText("words.txt"))
{
    // Process both files
}           

在一個 using 語句中聲明多個執行個體時,它們将按聲明的相反順序釋放。還可以将 using 語句和聲明與适用于可釋放模式的 ref 結構的執行個體一起使用。 也就是說,它有一個執行個體 Dispose 方法,該方法是可通路、無參數的并且具有 void 傳回類型。

using 語句也可以采用expression形式:

StreamReader reader = File.OpenText(filePath);

using (reader)
{
    // Process file content
}           

其中 expression(此例中的reader) 會生成可釋放執行個體。

二、using 指令

using 指令允許使用在命名空間中定義的類型,而無需指定該類型的完全限定命名空間。 using 指令以基本形式從單個命名空間導入所有類型,如下例所示:

using System.Text;           

可以通過 using 别名指令為名稱空間或類型建立别名。

using Project = PC.MyCompany.Project;           

可對 using 别名指令使用 global 修飾符。

沒有 global 修飾符的 using 指令的作用域是顯示它的檔案。

可能出現 using 指令的位置:

  • 源代碼檔案的開頭,位于任何命名空間或類型聲明之前。
  • 在任何命名空間中,但在該命名空間中聲明的任何命名空間或類型之前,除非使用 global 修飾符,在這種情況下,指令必須出現在所有命名空間和類型聲明之前。

否則,将生成編譯器錯誤 CS1529。

建立 using 指令,以便在命名空間中使用類型而不必指定命名空間。 using 指令不為你提供對嵌套在指定命名空間中的任何命名空間的通路權限。 命名空間分為兩類:使用者定義的命名空間和系統定義的命名空間。 使用者定義的命名空間是在代碼中定義的命名空間。

可以将兩個修飾符應用于 using 指令:

  • global 修飾符與将同一 using 指令添加到項目中的每個源檔案具有相同的效果。 此修飾符是在 C# 10 中引入的。
  • static 修飾符從單個類型中導入該 static 成員和嵌套類型,而不是導入命名空間中的所有類型。

global 修飾符

向 using 指令添加 global 修飾符意味着 using 将應用于編譯中的所有檔案(通常是一個項目)。 global using 指令被添加到 C# 10 中。 其文法為:

global using <fully-qualified-namespace>;           

其中 fully-qualified-namespace 是命名空間的完全限定名稱,其類型可以在不指定命名空間的情況下引用。

global using 指令可以出現在任何源代碼檔案的開頭。 單個檔案中的所有 global using 指令必須出現在以下内容之前:

  • 沒有 using 修飾符的所有 global 指令。
  • 檔案中的所有命名空間和類型聲明。

可以将 global using 指令添加到任何源檔案中。 通常,你需要将它們保留在一個位置。 global using 指令的順序并不重要,無論是在單個檔案中,還是在檔案之間。

global 修飾符可以與 static 修飾符合并。 global 修飾符可以應用于 using 别名指令。 在這兩種情況下,指令的作用域都是目前編譯中的所有檔案。

以下示例允許在項目中的所有檔案中使用 System.Math 中聲明的所有方法:

global using static System.Math;           

還可以通過将 <Using> 項添加到項目檔案(例如 <Using Include="My.Awesome.Namespace" />)來全局包含命名空間。

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 指令來提供 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 别名

建立 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.           

繼續閱讀