(ゲームじゃない方の)
Switchで遊びたい話
masanori_msl@ .NET 5リリース記念 C# Tokyo イベント
About me
 Name:Masui Masanori
 Work:無茶ぶり処理班
 Twitter:https://twitter.com/masanori_msl
 Blog:http://mslgt.hatenablog.com/
https://dev.to/masanori_msl
※なおゲーム機の方は持ってません
今回のお話
C# 7以降でSwitchが
色々パワーアップしてるらしい
...ので試してみた
いままで(定数パターン)
class Program
{
static void Main(string[] args)
{
int id = 1;
switch(id)
{
case 0:
Console.WriteLine("Id was 0");
break;
case 1:
Console.WriteLine("Id was 1");
break;
default:
Console.WriteLine("Not found");
break;
}
}
}
これが実行される
定数を条件として処理を分岐
新しく追加されたもの(型パターン)
public record RecordSample(int Id, string Name);
static void Main(string[] args)
{
RecordSample r = new (3, "Hello");
Play((object)r);
}
private static void Play(object target)
{
switch(target)
{
case RecordSample r:
Console.WriteLine($"Record Id: {r.Id} Name: {r.Name}");
break;
default:
Console.WriteLine("Not found");
break;
}
}
これが実行される
• 型を条件として処理を分岐。変換が可能ならその分岐の処理が実行される + インスタンス生成
• 定数パターンと型パターンは混在できる
型パターンはwhenでさらに細分化できる
private static void Play(object target)
{
switch(target)
{
case RecordSample r when r.Id < 0:
Console.WriteLine($"Record1 Id: {r.Id} Name: {r.Name}");
break;
case RecordSample r when r.Id >= 1:
Console.WriteLine($"Record2 Id: {r.Id} Name: {r.Name}");
break;
case RecordSample r:
Console.WriteLine($"Record Id: {r.Id} Name: {r.Name}");
break;
default:
Console.WriteLine("Not found");
break;
}
}
これが実行される
変換が可能 + when の条件に合致すればその分岐の処理が実行される
さらに
C# 8からはさらにswitch式が登場
いままで(switch文)
static void Main(string[] args)
{
int id = 1;
string message = GetMessage(id);
}
private static string GetMessage(int id)
{
switch(id)
{
case 0:
return "ID was 0";
case 1:
return "ID was 1";
default:
return "Other";
}
}
これが実行される
switch式を使う
static void Main(string[] args)
{
int id = 1;
string message = id switch
{
0 => "ID was 0",
1 => "ID was 1",
_ => "Other"
};
}
これが実行される
• 使えるのはswitch文と同様定数パターンと型パターン
• 初見だとびっくりする(した)が、三項演算子やNull合体演算子を考えると納得できる気が
疑問
whenをたくさん使うと
インスタンスもたくさん作られる?
先ほどのコード
private static void Play(object target)
{
switch(target)
{
case RecordSample r when r.Id < 0:
Console.WriteLine($"Record1 Id: {r.Id} Name: {r.Name}");
break;
case RecordSample r when r.Id >= 1:
Console.WriteLine($"Record2 Id: {r.Id} Name: {r.Name}");
break;
case RecordSample r:
Console.WriteLine($"Record Id: {r.Id} Name: {r.Name}");
break;
default:
Console.WriteLine("Not found");
break;
}
}
target が RecordSample に変換できて、かつ Id = 0 だった場合、インスタンスは3つ作られる?
答え
生成されるインスタンスは一つ
どう実行されるのか
VS Code拡張版ILSpy先生に聞いてみた
(https://marketplace.visualstudio.com/items?itemName=icsharpcode.ilspy-vscode)
こんな風になっているらしい
private static void Play(object target)
{
RecordSample recordSample = target as RecordSample;
if ((object)recordSample != null)
{
if (recordSample.Id < 0)
{
Console.WriteLine($"Record1 Id: ~省略~
return;
}
RecordSample recordSample2 = recordSample;
if (recordSample2.Id >= 1)
{
Console.WriteLine($"Record2 Id: ~省略~
return;
}
RecordSample recordSample3 = recordSample;
Console.WriteLine($"Record Id: ~省略~
}
~省略~
詳しくはWebで
パターン マッチング - C# ガイド | Microsoft Docs
https://docs.microsoft.com/ja-jp/dotnet/csharp/pattern-matching
switch 式 - C# リファレンス | Microsoft Docs
https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/switch-expression
パターン マッチング - C# によるプログラミング入門 | ++C++; // 未確認飛行 C
https://ufcpp.net/study/csharp/datatype/patterns/
C# 8.0 パターン マッチング | ++C++; // 未確認飛行 C ブログ
https://ufcpp.net/blog/2018/12/cs8patterns/
Do more with patterns in C# 8.0 | .NET Blog
https://devblogs.microsoft.com/dotnet/do-more-with-patterns-in-c-8-0/
C# 8.0 - C# 8.0 でのパターン マッチング | Microsoft Docs
https://docs.microsoft.com/ja-jp/archive/msdn-magazine/2019/may/csharp-8-0-pattern-matching-
in-csharp-8-0
Programming C# 8.0
https://www.oreilly.com/library/view/programming-c-80/9781492056805/
少し遊んでみる
TypeScriptっぽく変換してみたい
どういうことか
TypeScript・・・というかJavaScriptは、
そのClass(or Type or Inteface)と同じプロパティを持っていれば、
同じものとしてふるまうことができる
ということでそれを真似してみる
• RecordSample に直接変換できる場合は変換する
• 変換できない場合でかつ同じプロパティを持っている場合は、
RecordSampleの同名のプロパティに値を入れたインスタンスを作る
変換用のメソッドを作る
public record RecordSample
{
public int Id { get; init; }
public string Name { get; init; } = "";
public RecordSample(int id, string name) { Id = id; Name = name; }
public static bool TryParse(object original, out RecordSample? result)
{
result = null;
var type = original.GetType();
var idValue = type.GetProperty("Id")?.GetValue(original)?.ToString();
var nameValue = type.GetProperty("Name")?.GetValue(original)?.ToString();
if(idValue == null || nameValue == null) { return false; }
if(int.TryParse(idValue, out var id) == false) { return false; }
result = new RecordSample(id, nameValue);
return true;
}
}
case を追加
private static void Play(object target)
{
switch(target)
{
case RecordSample r:
Console.WriteLine($"Record Id: {r.Id} Name: {r.Name}");
break;
case var o when RecordSample.TryParse(o, out var r):
Console.WriteLine($"Property Id: {r!.Id} Name: {r!.Name}");
break;
default:
Console.WriteLine("Not found");
break;
}
}
RecordSampleに変換できず、
かつ同じプロパティを持っていれば
ここが実行されるはず
呼び出し
public class ClassSample
{
public int Id { get; set; }
public string Name { get; set; } = "";
}
static void Main(string[] args)
{
var c = new ClassSample
{
Id = 0,
Name = "Hello"
};
Play((object)c);
}
同じプロパティを持つClassSampleというクラスを作って先ほどのswitchに渡す
結果
Property Id: 0 Name: Hello
疑問
どこで使おう?
便利そうだけれども
• あんまりobjectで渡して型変換ってしない気がする
(ライブラリを作る場合は重宝するかも?)
• switch式の方は細かく使えそうかも
• ClosedXML(https://github.com/closedxml/closedxml)と
組み合わせると良さそう?
Thank you!

(ゲームじゃない方の)switchで遊びたい話