🐕

【C#】switch式でswitch文を再現する

に公開

switch式の問題点

  • C#のswitch式はC# 8.0で導入
  • C由来の古いswitch文を置き換えていきたいけど…
  • "式(expression)"なので完全にswitch文を置き換えられない

https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/switch-expression

https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/language-specification/statements#1383-the-switch-statement

switch文を置き換えられない理由

現状できない(複数行)
/*
_ = cond switch
{
	< 0 => {
		// 複数行の処理A
	},
	> 50 => {
		// 複数行の処理B
	},
	_ => 0,
};
*/

https://github.com/dotnet/csharplang/issues/9243

無理やり再現してみる

無理やり再現:パターンA

Func<T>を宣言して即時実行する

var cond = new Random().Next(0, 100);

_ = cond switch
{
	< 0 => new Func<int>( () => {
		// 複数行の処理A
		return default;
	})(),
	> 50 => new Func<int>( ()  => {
		// 複数行の処理B
		return default;
	})(),
	_ => 0,
};
  • switch式の戻り値は利用しないので_に渡す

  • new Func<T>Func<T>のデリゲートを作成

    • 型は何でもいいが、一番短いint
  • 最後に()付けて即時実行

  • 引数にラムダ式を渡してその中で複数行処理

  • return default;で戻り値を必ず返すようにする

  • メリットデメリット

    • メリット
      • 特に何の用意もなくできる
    • デメリット
      • やっぱり長い…

無理やり再現:パターンB

var cond = new Random().Next(0, 100);

//短くかける用の関数を定義する
static object F(Action<object> f) { f(null); return default; }

_ = cond switch
{
	< 0 => F(_ => {
		/* 複数行の処理A */
	}),
	> 50 => F(_ => {
		/* 複数行の処理B */
	}),
	_ => 0,
};
  • 短くかける用の関数F()を定義して使う
  • ラムダ式を短くかける(_ => {})ようにF()の引数はAction<T>
    • Action<T> = 引数を一つとる
  • return default;F()の中に追いやる
  • メリットデメリット
    • メリット
      • だいぶ短くかける
    • デメリット
      • どこでも使えるようにするにはまだ工夫がいる

無理やり再現:パターンC

Util.csなどに短くかける関数の定義を持ってゆく
namespace MyNamespace;
public static class Util
{
    public static object F(Action<object> f) { f(null); return default; }
}

コード中で指定

Program.csとかGlobalUsing.csとかに定義(C#10.0以降)
global using static MyNamespace.Util;

https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/proposals/csharp-10.0/globalusingdirective

csprojで指定

.NET 8.0 以降
<ItemGroup>
    <Using Include="MyNamespace.Util" Static="true" />
</ItemGroup>

https://learn.microsoft.com/ja-jp/dotnet/core/project-sdk/msbuild-props#using

実際に使うとき

実際に使うとき
var cond = new Random().Next(0, 100);
_ = cond switch
{
	< 0 => F(_ => {
		/* 複数行の処理A */
	}),
	> 50 => F(_ => {
		/* 複数行の処理B */
	}),
	_ => 0,
};
  • パターンBをどこからでもつかえるようにした
  • コード中またはcsproj中で指定できる
    • コード中で指定:C# 10.0 以降
    • csprojで指定:.NET 8.0 以降

Discussion