Uzzu::Blog

Software Design, and my life.


C#でパターンマッチング

Tags: csharp

この記事はC# Advent Calendar 2014の23日目のエントリです。 22日目はC#のIntの話でした。

クライアントアプリを書いていると何かとステートフルになり、かつ状態毎の描画もころころ変わります。さらに、WPFの恩恵を受けられないC#でかつ古いmono環境化(決してゲームエンジンは指していませんね?)では、かなり悲しみを背負う事になります。なにも悲しみを背負うので、Rxが再実装されたり、データバインディング機構を再実装したりします。辛い。

ステートフルかつ何も恩恵が得られないとなれば、無限に繁殖するifとswitch-caseですね。ifはさておき、C#のswitch-caseは非常にシンプルなんですが、シンプルが故にかゆい所に手が届かなくて辛い。 そもそも状態と条件分岐は○○モデルにしましょうよ!的な所で、参照型Enumを作ったりします。JavaのEnumっぽい何かですね。

ドメイン層のモデルにおいては、正しく責務が与えられているはずなので、この参照型Enumというアプローチは有効なんですが、UI層でも同様に、Aggregate(ViewModel)の状態とその描画方法を見通しよく記述できるかというと、必ずしもそうでもないんじゃないかなというのが経験上の持論です。(異論は認める)

そこで条件分岐なんとかしようやと、とりあえず試しにパターンマッチング実装してみた。 ぶっちゃけ途中掛けで、full-AOT問題と、あとはgenerics周りの問題を解決したら、原型をとどめてないインターフェースになりそう。

Case.cs

<<<追記>>>

型推論効く形にちょいと手直ししました。各APIの利便性を優先、した結果、型推論効かないときはコンパイルエラーになっちゃうけどまあそこは手前で型引数渡せば良いのでしゃーなし。 だいぶスッキリはしたけど、full-AOTは未着手。

<<<追記おわり>>>

Linqを書くときみたいに三項演算子をモリモリ書いていくのもなんだか違和感あるし、でも条件分岐の判定部分はPredicateにしたいし、とか詰め込みながらモリモリと書いていき、 だんだん作るのが楽しくなってきて、もうちょっと便利にとか、型推論効かせて記述を楽にしようとかしてたら、結果としてたくさんCS0411を引きまくって一旦元に戻しました。

UIロジックも関数型ライクなDSLっぽくっていうかF#で書いたらこのご時世でももうちょっとマシなコードになりそうな気がするんだよなあと思ったところで終了です。 下はサンプルですが、もうちょっと複雑なのもいけるのでまあテストとか読んでみてください

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class CharacterStatusView { private Case<CharacterStatus>.IMatcher characterStatusRenderer; public void Initialize() { characterStatusRenderer = CaseAction .When(status => status.Dead , RenderAsDead) .When(status => status.Danger, RenderAsDanger) .When(status => status.Warning, RenderAsWarning) .Default(RenderAsSafety); } void OnCharacterStatusChanged(object sender, PropertyChangedEventArgs<CharacterStatus> e) { characterStatusRenderer.Apply(e.NewValue); } void RenderAsDead(CharacterStatus status) { // 死んだお } void RenderAsDanger(CharacterStatus status) { // ピンチだお } void RenderAsWarning(CharacterStatus status) { // そろそろやばいお } void RenderAsSafety(CharacterStatus status) { // 元気モリモリ } }

Comments