C# の Deconstruct

C#

Deconstruct は、C# 7.0 で導入された「分解代入(deconstruction)」を実現するパターンです。特定のシグネチャを持つ Deconstruct メソッドを型に定義(または組み込み)することで、以下のコードのようにオブジェクトの複数の値をまとめて変数へ取り出せるようになります。

C#
var (x, y) = someObject;
(x, y) = someObject;
(int a, string b) = someObject;

仕組みとメソッドシグネチャ

型が分解をサポートするには、次のいずれかの Deconstruct メソッドを持っている必要があります。

C#
// out パラメーターで取り出す値の数と型を宣言
public void Deconstruct(out T1 first, out T2 second, /* … */) 
{
    first  = /*…*/;
    second = /*…*/;
    // …
}
  • 戻り値は void
  • パラメーターは out 修飾のプリミティブ型、参照型、あるいはユーザー定義型
  • オーバーロード可能(同じ型に複数の Deconstruct があっても OK)

組み込みで Deconstruct を持つ型

タプル系

  • System.ValueTuple: C# 7.0 の言語機能で分解構文をサポート
  • System.Tuple: .NET Standard 2.1/.NET Core 2.0 以降、拡張メソッドで Deconstruct が提供される

レコード

C# 9 以降の 位置指定レコード (record R(T1 a, T2 b);) は、コンストラクター引数に応じた Deconstruct(out T1, out T2)自動生成する

その他

KeyValuePairDictionaryEntry など、標準ライブラリが拡張メソッドで Deconstruct を用意している型もある。

カスタム型での分解サポート

任意のクラスや構造体にも Deconstruct を実装できます。例として平面上のポイント型を分解可能にする場合を示します。

C#
public struct Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y) => (X, Y) = (x, y);

    // Deconstruct を実装
    public void Deconstruct(out int x, out int y) 
        => (x, y) = (X, Y);
}

var p = new Point(3, 5);
var (a, b) = p;  
// a == 3, b == 5

パラメーター数を増やしたり、別の型への分解をオーバーロードすることも可能です。

分解構文

分解宣言 (Deconstruction Declaration)

C#
var (city, population, area) = QueryCityData();  

varキーワードで変数宣言と分解を同時に実行します。

分解代入 (Deconstruction Assignment)

C#
(string name, int age) = GetPerson();  
// 既存変数があれば 型指定も可能
(x, y) = (y, x);  
// swap

部分的な破棄 (Discards)

C#
var (_, year, _) = GetStats();  

_ を使うとその要素を無視します。

C#10での拡張

C#10では分解代入と分解宣言が混在できるようになりました。

C#
// x を宣言
int x;
// 1 つ目は既存変数へ、2 つ目は新規にvar宣言して分解
(x, var y) = GetPoint();  

要素ごとに var と明示型を混在できます。
タプルの各要素に対し、個別に型を指定したり var に切り替えたりできます。

C#
// 1: string、2: var、3: DateTime
(string name, var count, DateTime ts) = FetchRecord();  

ただしネストした「途中での宣言」は依然として不可です。以下のように「右辺の式の途中で var 宣言をはさむ」コードは、C# 10 でもコンパイルエラーになります。

C#
int x, y;
// NG!: 
(x, var u) = (var v, y) = (1, 2);  // 右辺の代入式内で var 宣言を使おうとするとエラー

コメント

タイトルとURLをコピーしました