Upgrade to Pro — share decks privately, control downloads, hide ads and more …

C# code refactoring with Scope Functions

6b37e5c21ba723dacede78124b25d5a8?s=47 Masaya Yashiro
September 26, 2018

C# code refactoring with Scope Functions

Kotlin で標準実装されているScope FunctionのうちのいくつかをC#(Unity) 向けに移植した話。

6b37e5c21ba723dacede78124b25d5a8?s=128

Masaya Yashiro

September 26, 2018
Tweet

Transcript

  1. スコープファンクションでコード の見通しを改善する 2018/09/26 Gotanda.unity #8 @yashims85

  2. 誰 • Twitter: @yashims85 • 会社: モバイルファクトリー@ 五反田 • Unity:

    趣味でさわってる • 最近気になる言語: Rust • 緩募: 継続するダイエット
  3. 質問です 皆さんはイケてないな。。。 と思いながら、そんなもんだろうと諦めて コードを書いていることはありませんか?

  4. • PBXProjectのプロパティ • 関数に外出しするほどではないちょっとし た計算 • Builder, Factoryパターンを採用するほど でもないちょっとした手順 •

    大量のnullチェックもしくは ?. 地獄 私がイケてなぁと思いながらコードを書 いているとき
  5. スコープファンクション もしかしたらスコープ ファンクションがそれ らの悩みを一助にな るかもしれません。

  6. スコープファンクション • Kotlinの文脈で登場する • (基本的に)引数にLambdaを1つとって、そ の中で自己参照をする • C#のExtensionで作った関数のように全部 のObjectに生えてる

  7. なるほど

  8. いくつか移植してみた CSharpScopeExtensions: https://github.com/yashims/CSharpScop eExtensions ここでスター欲しいって言えば 優しい人がしてくれるって聞きました

  9. 導入方法 • releaseにunitypackageがおいてあるので ご自由にお使い下さい • もしくは簡単なのでコピペ

  10. 実際の実装 今回は諸般の事情により2つだけ紹介

  11. Also

  12. Also public static T Also<T>(this T self, Action<T> action) {

    action(self); return self; }
  13. Also public static T Also<T>(this T self, Action<T> action) {

    action(self); return self; } ジェネリクスのExtensionなので、どの Objectからも呼び出せる
  14. Also public static T Also<T>(this T self, Action<T> action) {

    action(self); return self; } 自分自身を引数に取る Actionに自分 自身を渡す
  15. Also public static T Also<T>(this T self, Action<T> action) {

    action(self); return self; } 戻り値として自分を返す。
  16. Let

  17. Let public static R Let<T, R>(this T self, Func<T, R>

    action) { return action(self); }
  18. Let public static R Let<T, R>(this T self, Func<T, R>

    action) { return action(self); } ジェネリクスのExtensionなので、どの Objectからも呼び出せる
  19. Let public static R Let<T, R>(this T self, Func<T, R>

    action) { return action(self); } 自分自身をFuncに渡し、Funcの戻り 値がLetの戻り値となる。
  20. それがどしたの

  21. 実際の使用感をみてみる

  22. Alsoの使い所 例えばインスタンスのプロパティ設 定

  23. Alsoを使わない void Start() { Transform t1 = GetComponent<Transform>(); Quaternion q1

    = new Quaternion(); q1.x = 0; q1.y = 0; q1.x = 0; t1.rotation = q1; }
  24. Alsoを使う void Start() { Transform t2 = GetComponent<Transform>().Also((it) => {

    it.rotation = new Quaternion().Also((that) => { that.x = 0; that.y = 0; that.z = 0; }); }); }
  25. Alsoを使う • Start()のスコープを一時変数で汚さない • いくつかの処理が記述されてるようならコレはメリット • 引数の変数名を`it`等の代名詞にしておけば、何に対して の処理かが明確になり、それ以外に言及するコードを避 けられる •

    コレを使えばPBXProjectの設定が超絶スッキリする
  26. Letの使い所 例えばLINQのSelectの様に使う

  27. Letを使わない Transform t = GetComponent<Transform>(); float distanceX = t.parent.transform.localPosition.x +

    t.localPosition.x;
  28. Letを使う float distanceX = GetComponent<Transform>().Let((it) => { return it.parent.transform.localPosition.x +

    it.localPosition.x; });
  29. Letを使う • LINQやRxのSelectと同じイメージで、Lambda内で変形を 行える • Alsoと同じく、一時変数でスコープを汚さない

  30. Letの使い所 例えばC#6.xのnullableの unwrapping

  31. Letを使わない string say(string comment) { return comment != null ?

    $"I said: {comment}" : ""; }
  32. Letを使う string say(string comment) { return comment?.Let(it => $"I said:

    {it}") ?? ""; }
  33. Letを使う • C#でのnull条件演算子「?.」は、nullチェックして、非null であればローカル変数に格納するのと同義 • それをLetの引数で受け取っているので、Letの中では Null安全な変数を自由に使える

  34. できなかったこと

  35. できなかったこと • Lambdaの中でthisコンテキストを変える • KotlinではLambdaの中ではthisがそれ自身になるス コープファンクションがある(例: Apply)

  36. Apply(実現できたら) public static T Apply<T>(this T self, Action action) {

    self.action(); return self; }
  37. Apply(実現できたら) public static T Apply<T>(this T self, Action action) {

    self.action(); return self; } ジェネリクスのExtensionなので、どの Objectからも呼び出せる
  38. Apply(実現できたら) public static T Apply<T>(this T self, Action action) {

    self.action(); return self; } TをthisとしてActionを呼び出す
  39. Apply(実現できたら) public static T Apply<T>(this T self, Action action) {

    self.action(); return self; } 自分自身を返す
  40. Applyを使えたら void Start() { Transform t2 = GetComponent<Transform>().Apply(() => {

    this.rotation = new Quaternion().Apply(() => { x = 0; y = 0; z = 0; }); }); } this以外の事について言及する余地 が無くなる
  41. まとめ

  42. まとめ • ScopeFunctionを使うと一時変数が減ら せ、コードの見通しが良くなる • ブロックで区切られることで、何について述 べられているコードかが簡潔になる • Lambdaの中でthisを変える方法があった ら誰か教えてください

  43. ありがとうございました