【C#】2つの変数の中身を入れ替える方法4選

2つの変数の中身を入れ替える方法を4種類紹介したいと思います。

(1) 昔ながらの方法

教科書に書いてあるやつ。最も一般的な方法。大抵の言語でこうやって入れ替えられる。

int a = 10;
int b = 20;

int _tmp = a;
a = b;
b = _tmp;
// > a=20, b=10

ローカル変数がひとつ必要になる。以下のように中カッコのスコープを切って処理する小技もある。

int a = 10;
int b = 20;
{
   int _tmp = a;
   a = b;
   b = _tmp;
}
// こうやって中カッコでくくると _tmp 変数自体がこの「}」を抜ける時に消えるので少し安心できる

但しこんな事するくらいなら細かくメソッドに分割したほうがマシ。

(2) Tupleを使う

C#7.0で導入されたタプルという機能を使用する。見た目が非常にシンプル。余計な変数が必要ない。ただしバージョン(環境)次第で使えないことがある。

  • 推奨環境条件
    • C#7.0
    • .NET Core 2.0以降
    • .NET Framework 2.7以降
    • IDEの構文サポート
int a = 10;
int b = 20;
(a, b) = (b, a);
// > a=20, b=10

ValueTuple に対する「()」(丸括弧)を使った省略記法をC#7.0以降から使用可能のためいったん Tuple にまとめて置換すると簡潔に記述できる。余計な変数が必要ない。コンパイルすると変数3つでスワップになるためやってることは古典的な方法と同じ。

これ以前のバージョンでも 追加パッケージを導入すれば ValueTuple は使える。VisualStudio 2017 + .NET4.5 では以下エラーが表示される。

エラー CS8179 定義済みの型 'System.ValueTuple`2' は定義、またはインポートされていません

Nuget から System.ValueTuple を追加するとエラーが収まる。

ViusalStudio2017 より前の環境では "Compilers 3.7.0" でコンパイラを変更することで使用できる。ただし IDE が構文サポートをしていないので赤い波線がでエラー表示が出るけどコンパイルはできる謎の環境になる場合がある。ただし可能であれば新しいVSに乗り換えたほうがいい。というかもしIDEが会社の金なら即座に乗り換えるべき。C#ならデメリット無いのでは?VC++はしらぬ。

(3) 外部のメソッドで入れ替える

この方法は言語バージョン依存が存在しないためどのC#でも使える。いちどどこかに定義すれば汎用的にバージョン依存も無く使いまわせるので定義しておいて損はない。

// ValueUtil.cs

public static class ValueUtil
{
    public static void Swap<T>(ref T a, ref T b)
    {
        T _tmp = a;
        a = b;
        b = _tmp;
    }
}

int a = 10;
int b = 20;
ValueUtil.Swap(ref a, ref b);
// > a=20, b=10

ref を引数に指定しないといけなのが若干気になる。そういうものなのであきらめる。

(4) 拡張メソッドで入れ替える(値型のみ)方法

この方法は構造体(値型)のみようできる。変数のメソッドとして実行できるのである意味直観的かも?

C#7.2以降で使用可能な構文を使用する。

  • 環境条件
    • C# 7.2
    • .NET Core2.1以降
    • .NET Framework 4.7以降 (オプションが指定必要)
    • ref 拡張メソッド構文のサポート
// StructExtension.cs

public static class StructExtension
{
    public static void Swap<T>(ref this T self, ref T tgt) where T : struct
    {
        T _tmp = self;
        self = tgt;
        tgt = _tmp;
    }
}

int a = 10;
int b = 20;
a.Swap(ref b);
// > a=20, b=10

クラスは言語仕様上の問題で入れ替え不可。これもやや新しい機能のため使用に制約がある。

余談だが、この書き方で拡張メソッドを記述すると全ての型にメソッドが追加されたように見せられるのでこの書き方が言語拡張の機能として悪用できる。ただし標準のC#と使用感が大きく変わるのでご利用は計画的に。

ほかにあるかな?

何かあれば随時。