破棄 - C# ガイドDiscards - C# Guide

C# 7.0 以降、C# は破棄をサポートしています。破棄は、アプリケーション コードで意図的に使用しない一時的なダミー変数です。Starting with C# 7.0, C# supports discards, which are temporary, dummy variables that are intentionally unused in application code. 破棄は、未割り当ての変数と同等です。つまり、値がありません。Discards are equivalent to unassigned variables; they do not have a value. 破棄変数は 1 つのみであり、破棄変数には記憶域も割り当てられないため、破棄を使用するとメモリの割り当てを減らすことができます。Because there is only a single discard variable, and that variable may not even be allocated storage, discards can reduce memory allocations. また、コードの意図がわかりやすくなるため、読みやすさと保守性が向上します。Because they make the intent of your code clear, they enhance its readability and maintainability.

変数を破棄と指定するには、変数名にアンダースコア (_) を指定します。You indicate that a variable is a discard by assigning it the underscore (_) as its name. たとえば、次のメソッド呼び出しは 3 つのタプルを返します。この 1 つ目と 2 つ目の値が破棄されます。area は以前に宣言した変数であり、GetCityInformation から返された、対応する 3 つ目のコンポーネントに設定されます。For example, the following method call returns a 3-tuple in which the first and second values are discards and area is a previously declared variable to be set to the corresponding third component returned by GetCityInformation:

(_, _, area) = city.GetCityInformation(cityName);

C# 7.0 では、破棄は次のコンテキストの割り当てでサポートされます。In C# 7.0, discards are supported in assignments in the following contexts:

  • タプルとオブジェクトの分解Tuple and object deconstruction.
  • isswitch によるパターン マッチング。Pattern matching with is and switch.
  • out パラメーターを使用したメソッドの呼び出し。Calls to methods with out parameters.
  • _ がスコープ内にない場合のスタンドアロンの _A standalone _ when no _ is in scope.

_ が有効な破棄の場合、その値を取得しようとすると、または代入演算で使用しようとすると、"名前 '_' は、現在のコンテキストに存在していません" というコンパイラ エラー CS0301 が生成されます。When _ is a valid discard, attempting to retrieve its value or use it in an assignment operation generates compiler error CS0301, "The name '_' does not exist in the current context". これは、_ に値が割り当てられておらず、記憶域の場所も割り当てることができないためです。This is because _ is not assigned a value, and may not even be assigned a storage location. 実際の変数であれば、前の例のように、複数の値を破棄できません。If it were an actual variable, you could not discard more than one value, as the previous example did.

タプルとオブジェクトの分解Tuple and object deconstruction

分解は、複数のタプルがあり、アプリケーション コードで一部のタプル要素を使用し、その他の要素を無視する場合に特に便利です。Discards are particularly useful in working with tuples when your application code uses some tuple elements but ignores others. たとえば、次の QueryCityDataForYears メソッドは、市区町村名、その地域、年、市区町村のその年の人口、2 つ目の年、市区町村のその 2 つ目の年の人口という 6 つのタプルを返します。For example, the following QueryCityDataForYears method returns a 6-tuple with the name of a city, its area, a year, the city's population for that year, a second year, and the city's population for that second year. この例は、2 つの年の間に変化した人口数を示しています。The example shows the change in population between those two years. タプルから使用できるデータのうち、市区町村の地域は使用しません。また、指定時に市区町村名と 2 つの日付はわかっています。Of the data available from the tuple, we're unconcerned with the city area, and we know the city name and the two dates at design-time. そのため、タプルに格納されている 2 つの人口値のみが必要であり、残りの値は破棄対象として処理できます。As a result, we're only interested in the two population values stored in the tuple, and can handle its remaining values as discards.

using System;
using System.Collections.Generic;

public class Example
{
   public static void Main()
   {
       var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

       Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
   }
   
   private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
   {
      int population1 = 0, population2 = 0;
      double area = 0;
      
      if (name == "New York City") {
         area = 468.48; 
         if (year1 == 1960) {
            population1 = 7781984;
         }
         if (year2 == 2010) {
            population2 = 8175133;
         }
      return (name, area, year1, population1, year2, population2);
      }

      return ("", 0, 0, 0, 0, 0);
   }
}
// The example displays the following output:
//      Population change, 1960 to 2010: 393,149

破棄を使用したタプルの分解の詳細については、「タプルとその他の型の分解」を参照してください。For more information on deconstructing tuples with discards, see Deconstructing tuples and other types.

また、クラス、構造体、またはインターフェイスの Deconstruct メソッドを使用すると、オブジェクトから特定セットのデータを取得し、分解することもできます。The Deconstruct method of a class, structure, or interface also allows you to retrieve and deconstruct a specific set of data from an object. 分解された値の一部のみが必要な場合は、破棄を使用できます。You can use discards when you are interested in working with only a subset of the deconstructed values. Person オブジェクトを 4 つの文字列 (名、姓、市区町村、州) に分解し、姓と州を破棄する例を次に示します。The following example deconstructs a Person object into four strings (the first and last names, the city, and the state), but discards the last name and the state.

using System;

public class Person
{
   public string FirstName { get; set; }
   public string MiddleName { get; set; }
   public string LastName { get; set; }
   public string City { get; set; }
   public string State { get; set; }

   public Person(string fname, string mname, string lname, 
                 string cityName, string stateName)
   {
      FirstName = fname;
      MiddleName = mname;
      LastName = lname;
      City = cityName;
      State = stateName;
   }   
   
   // Return the first and last name.
   public void Deconstruct(out string fname, out string lname)
   {
      fname = FirstName;
      lname = LastName;
   }

   public void Deconstruct(out string fname, out string mname, out string lname)
   {
      fname = FirstName;
      mname = MiddleName;
      lname = LastName;
   }

   public void Deconstruct(out string fname, out string lname, 
                           out string city, out string state)
   {
      fname = FirstName;
      lname = LastName;
      city = City;
      state = State;
   }                           
}

public class Example
{
   public static void Main()
   {
       Person p = new Person("John", "Quincy", "Adams", "Boston", "MA");

       // <Snippet1>
       // Deconstruct the person object.
       var (fName, _, city, _) = p;
       Console.WriteLine($"Hello {fName} of {city}!");
       // The example displays the following output:
       //      Hello John of Boston!       
       // </Snippet1>
   }
}
// The example displays the following output:
//    Hello John Adams of Boston, MA!

破棄を使用したユーザー定義型の分解の詳細については、「タプルとその他の型の分解」を参照してください。For more information on deconstructing user-defined types with discards, see Deconstructing tuples and other types.

switchis を使用したパターン マッチングPattern matching with switch and is

破棄パターンは、is キーワードと switch キーワードを使用したパターン マッチングで使用できます。The discard pattern can be used in pattern matching with the is and switch keywords. 各式は常に破棄パターンと一致します。Every expression always matches the discard pattern.

is ステートメントを使用して、オブジェクトが IFormatProvider 実装を提供しているかどうかを判断し、オブジェクトが null かどうかをテストする ProvidesFormatInfo メソッドの定義例を次に示します。The following example defines a ProvidesFormatInfo method that uses is statements to determine whether an object provides an IFormatProvider implementation and tests whether the object is null. また、破棄パターンを使用して、その他の任意の型の null 以外のオブジェクトを処理します。It also uses the discard pattern to handle non-null objects of any other type.

using System;
using System.Globalization;

public class Example
{
   public static void Main()
   {
      object[] objects = { CultureInfo.CurrentCulture, 
                           CultureInfo.CurrentCulture.DateTimeFormat, 
                           CultureInfo.CurrentCulture.NumberFormat,
                           new ArgumentException(), null };
      foreach (var obj in objects)
         ProvidesFormatInfo(obj);
   }

   private static void ProvidesFormatInfo(object obj)         
   {
      switch (obj)
      {
         case IFormatProvider fmt:
            Console.WriteLine($"{fmt} object");
            break;
         case null:
            Console.Write("A null object reference: ");
            Console.WriteLine("Its use could result in a NullReferenceException");
            break;
         case object _:
            Console.WriteLine("Some object type without format information");
            break;
      }
   }
}
// The example displays the following output:
//    en-US object
//    System.Globalization.DateTimeFormatInfo object
//    System.Globalization.NumberFormatInfo object
//    Some object type without format information
//    A null object reference: Its use could result in a NullReferenceException

out パラメーターを使用したメソッドの呼び出しCalls to methods with out parameters

Deconstruct メソッドを呼び出してユーザー定義型 (クラス、構造体、またはインターフェイスのインスタンス) を分解する場合、個々の out 引数の値を破棄できます。When calling the Deconstruct method to deconstruct a user-defined type (an instance of a class, structure, or interface), you can discard the values of individual out arguments. また、out パラメーターを指定して任意のメソッドを呼び出すときに、out 引数の値を破棄することもできます。But you can also discard the value of out arguments when calling any method with an out parameter.

次の例では、DateTime.TryParse(String, out DateTime) メソッドを呼び出して、現在のカルチャで日付の文字列表現が有効かどうかを判断します。The following example calls the DateTime.TryParse(String, out DateTime) method to determine whether the string representation of a date is valid in the current culture. この例では、日付文字列の検証のみが目的で、解析して日付を抽出する処理は行わないため、メソッドの out 引数は破棄されます。Because the example is concerned only with validating the date string and not with parsing it to extract the date, the out argument to the method is a discard.

using System;

public class Example
{
   public static void Main()
   {
      string[] dateStrings = {"05/01/2018 14:57:32.8", "2018-05-01 14:57:32.8",
                              "2018-05-01T14:57:32.8375298-04:00", "5/01/2018",
                              "5/01/2018 14:57:32.80 -07:00", 
                              "1 May 2018 2:57:32.8 PM", "16-05-2018 1:00:32 PM", 
                              "Fri, 15 May 2018 20:10:57 GMT" };
      foreach (string dateString in dateStrings)
      {
         if (DateTime.TryParse(dateString, out _)) 
            Console.WriteLine($"'{dateString}': valid");
         else
            Console.WriteLine($"'{dateString}': invalid");
      }
   }
}
// The example displays output like the following:
//       '05/01/2018 14:57:32.8': valid
//       '2018-05-01 14:57:32.8': valid
//       '2018-05-01T14:57:32.8375298-04:00': valid
//       '5/01/2018': valid
//       '5/01/2018 14:57:32.80 -07:00': valid
//       '1 May 2018 2:57:32.8 PM': valid
//       '16-05-2018 1:00:32 PM': invalid
//       'Fri, 15 May 2018 20:10:57 GMT': invalid

スタンドアロンの破棄A standalone discard

スタンドアロンの破棄を使用して、無視対象として任意の変数を指定できます。You can use a standalone discard to indicate any variable that you choose to ignore. 次の例では、スタンドアロンの破棄を使用して、非同期操作で返される Task オブジェクトを無視します。The following example uses a standalone discard to ignore the Task object returned by an asynchronous operation. この結果、この処理が完了するときにスローされる例外が抑制される効果があります。This has the effect of suppressing the exception that the operation throws as it is about to complete.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      ExecuteAsyncMethods().Wait();
   }

   private static async Task ExecuteAsyncMethods()
   {    
      Console.WriteLine("About to launch a task...");
      _ = Task.Run(() => { var iterations = 0;  
                           for (int ctr = 0; ctr < int.MaxValue; ctr++)
                              iterations++;
                           Console.WriteLine("Completed looping operation...");
                           throw new InvalidOperationException();
                         });
      await Task.Delay(5000);                        
      Console.WriteLine("Exiting after 5 second delay");
   }
}
// The example displays output like the following:
//       About to launch a task...
//       Completed looping operation...
//       Exiting after 5 second delay

_ は有効な識別子でもある点に注意してください。Note that _ is also a valid identifier. サポートされるコンテキスト以外で _ を使用すると、破棄対象ではなく、有効な変数として扱われます。When used outside of a supported context, _ is treated not as a discard but as a valid variable. _ という識別子が既にスコープ内にある場合、スタンドアロンの破棄として _ を使用すると、次のような結果になります。If an identifier named _ is already in scope, the use of _ as a standalone discard can result in:

  • 意図した破棄の値を割り当てることで、スコープ内の _ 変数の値が誤って変更される。Accidental modification of the value of the in-scope _ variable by assigning it the value of the intended discard. 次に例を示します。For example:

    private static void ShowValue(int _)
    {
       byte[] arr = { 0, 0, 1, 2 };
       _ = BitConverter.ToInt32(arr, 0);
       Console.WriteLine(_);
    }
    // The example displays the following output:
    //       33619968
    
  • タイプ セーフに違反するコンパイラ エラーが発生する。A compiler error for violating type safety. 次に例を示します。For example:

    private static bool RoundTrips(int _)
    {
       string value = _.ToString();
       int newValue = 0;
       _ = Int32.TryParse(value, out newValue);
       return _ == newValue;
    }
    // The example displays the following compiler error:
    //      error CS0029: Cannot implicitly convert type 'bool' to 'int'   
    
  • コンパイラ エラー CS0136 "ローカルまたはパラメーター '_' は、その名前が外側のローカルのスコープでローカルやパラメーターの定義に使用されているため、このスコープでは宣言できません" が発生する。Compiler error CS0136, "A local or parameter named '_' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter." 次に例を示します。For example:

    public void DoSomething(int _) 
    {
     var _ = GetValue(); // Error: cannot declare local _ when one is already in scope
    }   
    // The example displays the following compiler error:
    // error CS0136: 
    //       A local or parameter named '_' cannot be declared in this scope 
    //       because that name is used in an enclosing local scope 
    //       to define a local or parameter   
    

関連項目See also