破棄は、アプリケーション コードで意図的に使用されないプレースホルダー変数です。 破棄は、未割り当ての変数と同等です。つまり、値がありません。 破棄は、コンパイラやコードを読み取る他のユーザーに意図を伝えます。式の結果を無視することを意図しました。 式の結果、タプル式の 1 つ以上のメンバー、メソッドへの out
パラメーター、またはパターン マッチング式のターゲットを無視できます。
破棄すると、コードの意図が明確になります。 破棄は、コードが変数を使用しないことを示します。 読みやすさと保守容易性を高めます。
変数が破棄であることを示す場合は、名前としてアンダースコア (_
) を割り当てます。 たとえば、次のメソッド呼び出しは、1 番目と 2 番目の値が破棄されるタプルを返します。
area
は、 GetCityInformation
によって返される 3 番目のコンポーネントに設定された、以前に宣言された変数です。
(_, _, area) = city.GetCityInformation(cityName);
「破棄」と言う機能を使用して、ラムダ式の未使用の入力パラメーターを示すことができます。 詳細については、「ラムダ式」の記事の「ラムダ式の入力パラメーター」セクションを参照してください。
_
が有効な破棄である場合、その値を取得しようとしたり、割り当て操作で使用しようとすると、コンパイラ エラー CS0103 "名前 '_' は現在のコンテキストに存在しません" が生成されます。 このエラーは、 _
に値が割り当てられず、ストレージの場所も割り当てられない可能性があるためです。 実際の変数の場合は、前の例のように、複数の値を破棄できませんでした。
タプルとオブジェクトの分解
分解は、複数のタプルがあり、アプリケーション コードで一部のタプル要素を使用し、その他の要素を無視する場合に便利です。 たとえば、次の QueryCityDataForYears
メソッドは、市区町村の名前、その地域、年、その年の市区町村の人口、2 年目の市区町村の人口を持つタプルを返します。 この例は、2 つの年の間に変化した人口数を示しています。 タプルから使用できるデータのうち、市区町村の地域は使用しません。また、指定時に市区町村名と 2 つの日付はわかっています。 そのため、タプルに格納されている 2 つの人口値のみが必要であり、残りの値は破棄対象として処理できます。
var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);
Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
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
破棄を使用したタプルの分解の詳細については、「タプルとその他の型の分解」を参照してください。
クラス、構造体、またはインターフェイスの Deconstruct
メソッドを使用すると、オブジェクトから特定のデータ セットを取得および分解することもできます。 分解された値の一部のみが必要な場合は、破棄を使用できます。 次の例では、 Person
オブジェクトを 4 つの文字列 (姓、市区町村、州) に分解しますが、姓と状態は破棄します。
using System;
namespace Discards
{
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;
}
}
class Example
{
public static void Main()
{
var p = new Person("John", "Quincy", "Adams", "Boston", "MA");
// Deconstruct the person object.
var (fName, _, city, _) = p;
Console.WriteLine($"Hello {fName} of {city}!");
// The example displays the following output:
// Hello John of Boston!
}
}
}
破棄を使用したユーザー定義型の分解の詳細については、「タプルとその他の型の分解」を参照してください。
switch
を使用したパターン マッチング
破棄パターンは、パターン マッチングでswitch 式と共に使用できます。
null
を含むすべての式は、常に破棄パターンと一致します。
次の例では、ProvidesFormatInfo
式を使用して、オブジェクトがswitch
実装を提供するかどうかを判断し、オブジェクトがIFormatProviderされているかどうかをテストするnull
メソッドを定義します。 また、破棄パターンを使用して、他の型の null 以外のオブジェクトを処理します。
object?[] objects = [CultureInfo.CurrentCulture,
CultureInfo.CurrentCulture.DateTimeFormat,
CultureInfo.CurrentCulture.NumberFormat,
new ArgumentException(), null];
foreach (var obj in objects)
ProvidesFormatInfo(obj);
static void ProvidesFormatInfo(object? obj) =>
Console.WriteLine(obj switch
{
IFormatProvider fmt => $"{fmt.GetType()} object",
null => "A null object reference: Its use could result in a NullReferenceException",
_ => "Some object type without format information"
});
// The example displays the following output:
// System.Globalization.CultureInfo 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
パラメーターを持つメソッドの呼び出し
Deconstruct
メソッドを呼び出してユーザー定義型 (クラス、構造体、またはインターフェイスのインスタンス) を分解する場合は、個々のout
引数の値を破棄できます。 ただし、out
パラメーターを使用して任意のメソッドを呼び出すときに、out
引数の値を破棄することもできます。
次の例では、 DateTime.TryParse(String, out DateTime) メソッドを呼び出して、日付の文字列表現が現在のカルチャで有効かどうかを判断します。 この例は日付文字列の検証のみに関係しており、日付を抽出するための解析には関係しないため、メソッドの out
引数は破棄です。
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
スタンドアロンの破棄
スタンドアロンの破棄を使用して、無視する変数を指定できます。 一般的な用途の 1 つは、引数が null でないことを保証するために割り当てを使用することです。 次のコードでは、破棄を使用して割り当てを強制しています。 代入の右側ではnull合体演算子を使用して、引数がSystem.ArgumentNullExceptionのときにnull
をスローします。 コードは割り当ての結果を必要としないため、破棄されます。 式は null チェックを強制します。 破棄することで意図が明確になるため、割り当ての結果は使用されません。
public static void Method(string arg)
{
_ = arg ?? throw new ArgumentNullException(paramName: nameof(arg), message: "arg can't be null");
// Do work with arg.
}
次の例では、スタンドアロン破棄を使用して、非同期操作によって返される Task オブジェクトを無視します。 タスクの割り当ての結果、この処理が完了するときにスローされる例外が抑制される効果があります。 意図が明確になります。 Task
を破棄し、その非同期操作から生成されたエラーを無視します。
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
破棄にタスクを割り当てずに、次のコードはコンパイラ警告を生成します。
private static async Task ExecuteAsyncMethods()
{
Console.WriteLine("About to launch a task...");
// CS4014: Because this call is not awaited, execution of the current method continues before the call is completed.
// Consider applying the 'await' operator to the result of the call.
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");
注
デバッガーを使用して上記の 2 つのサンプルのいずれかを実行した場合、デバッガーは例外がスローされたときにプログラムを停止します。 デバッガーがアタッチされていない場合、どちらの場合も例外は自動的に無視されます。
_
も有効な識別子です。 サポートされているコンテキストの外部で使用すると、 _
は破棄としてではなく、有効な変数として扱われます。
_
という名前の識別子が既にスコープ内にある場合、スタンドアロンの破棄として_
を使用すると、次の結果が得られます。
- スコープ内の
_
変数の値を、意図した破棄の値を割り当てることで誤って変更してしまう。 例えば次が挙げられます。private static void ShowValue(int _) { byte[] arr = [0, 0, 1, 2]; _ = BitConverter.ToInt32(arr, 0); Console.WriteLine(_); } // The example displays the following output: // 33619968
- 型の安全性に違反した場合のコンパイラ エラー。 例えば次が挙げられます。
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'
こちらも参照ください
.NET