🪛

【C#】ifの条件部分で変数定義する小技

2023/07/01に公開
2

ある日のこと

コードを書いていて、以下のような書き方になるパターンがあった
※雰囲気コード

if (isHoge)
{
    DoHoge();
}
else
{
    var item = inventory.GetItem(id);
    if (item != null)
        DoFuga(item);
}

何が残念かって、elseでスコープができて更に中でifすることでスコープが1段余計に深くなってしまっている部分である

できれば↓みたいに書きたかった

if (isHoge)
{
    DoHoge();
}
else if ((var item = inventory.GetItem(id)) != null)
{
    DoFuga(item);
}

が、if条件部分で変数の定義はできない(コンパイルエラーになる)

is を使えばできなくはないですが、明示的に型を書かないといけないのと、なんか少し違和感が...

if (isHoge)
{
    DoHoge();
}
else if (inventory.GetItem(id) is Item item)
{
    DoFuga(item);
}

そして↓みたいに前もって変数を定義しておけば代入で似たようなことはできるけど、
varが使えないし変数宣言が離れてしまって気持ち悪い・・・

Item item; // ここで宣言しておく (varつかえぬ
if (isHoge)
{
    DoHoge();
}
// ここで初期化することでelse ifに繋げられるが...
else if ((item = inventory.GetItem(id)) != null)
{
    DoFuga(item);
}

解決策

個人的に完全に盲点だったのですが、is演算子にvarで受けるパターンが存在していました
※コメントにて jun1s 様に教えていただきました。感謝!!

https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/patterns?source=recommendations#var-pattern

if (isHoge)
{
    DoHoge();
}
else if (inventory.GetItem(id) is var item && item != null)
{
    DoFuga(item);
}

できましたワー!


以下に、varパターンを教えていただく前に使っていた方法を残しておきます。
※C#7.0の機能が使えない場合はこちらで対応可能です。

関数のout引数を使った解決方法

関数のout引数であれば ifの条件式中にも変数を定義できるので、拡張関数を使えば解決ができる。

↓こんな拡張関数を作って・・・

CommonExtensions.cs
public static class CommonExtensions
{
    public static T DeclVar<T>(this T value, out T variable)
    {
        variable = value;
        return variable;
    }
}

↓こうじゃ!

if (isHoge)
{
    DoHoge();
}
else if (inventory.GetItem(id).DeclVar(out var item) != null)
{
    DoFuga(item);
}

やりましたワー!

Discussion

Jun-ichi ShindeJun-ichi Shinde

こんにちは。気持ちよくコードから余計な記述や変数のスコープを排除できると気持ちいいですよね。

ご希望に沿えるコードではないかもしれませんが、次のコードで同様のことができるかもしれません。
Itemで受けた方がnullチェックも省略できて良い気はしますが、型名かかないといけないの、ちょっと嫌ですよね。

if (isHoge)
{
    DoHoge();
}
else if (inventory.GetItem(id) is var item && item != null)
{
    DoFuga(item);
}

C#9.0以降だと、is not null が使えるのでこう書けます。

if (isHoge)
{
    DoHoge();
}
else if (inventory.GetItem(id) is var item && item is not null)
{
    DoFuga(item);
}
TRSTRS

こんにちは、コメントありがとうございます!
is varのパターンマッチング、完全に盲点でした・・・!
汎用の拡張関数はあまり使いたくなかったのでめちゃくちゃ嬉しいです。
後ほど記事を更新させていただきます。