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

C# code refactoring with Scope Functions

Masaya Yashiro
September 26, 2018

C# code refactoring with Scope Functions

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

Masaya Yashiro

September 26, 2018
Tweet

More Decks by Masaya Yashiro

Other Decks in Technology

Transcript

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

    View full-size slide


  2. ● Twitter: @yashims85
    ● 会社: モバイルファクトリー@
    五反田
    ● Unity: 趣味でさわってる
    ● 最近気になる言語: Rust
    ● 緩募: 継続するダイエット

    View full-size slide

  3. 質問です
    皆さんはイケてないな。。。
    と思いながら、そんなもんだろうと諦めて
    コードを書いていることはありませんか?

    View full-size slide

  4. ● PBXProjectのプロパティ
    ● 関数に外出しするほどではないちょっとし
    た計算
    ● Builder, Factoryパターンを採用するほど
    でもないちょっとした手順
    ● 大量のnullチェックもしくは ?. 地獄
    私がイケてなぁと思いながらコードを書
    いているとき

    View full-size slide

  5. スコープファンクション
    もしかしたらスコープ
    ファンクションがそれ
    らの悩みを一助にな
    るかもしれません。

    View full-size slide

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

    View full-size slide

  7. なるほど

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  14. Also
    public static T Also(this T self, Action action)
    {
    action(self);
    return self;
    }
    戻り値として自分を返す。

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  17. Let
    public static R Let(this T self, Func action)
    {
    return action(self);
    }
    自分自身をFuncに渡し、Funcの戻り
    値がLetの戻り値となる。

    View full-size slide

  18. それがどしたの

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  21. Alsoを使わない
    void Start()
    {
    Transform t1 = GetComponent();
    Quaternion q1 = new Quaternion();
    q1.x = 0;
    q1.y = 0;
    q1.x = 0;
    t1.rotation = q1;
    }

    View full-size slide

  22. Alsoを使う
    void Start()
    {
    Transform t2 = GetComponent().Also((it) =>
    {
    it.rotation = new Quaternion().Also((that) =>
    {
    that.x = 0;
    that.y = 0;
    that.z = 0;
    });
    });
    }

    View full-size slide

  23. Alsoを使う
    ● Start()のスコープを一時変数で汚さない
    ● いくつかの処理が記述されてるようならコレはメリット
    ● 引数の変数名を`it`等の代名詞にしておけば、何に対して
    の処理かが明確になり、それ以外に言及するコードを避
    けられる
    ● コレを使えばPBXProjectの設定が超絶スッキリする

    View full-size slide

  24. Letの使い所
    例えばLINQのSelectの様に使う

    View full-size slide

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

    View full-size slide

  26. Letを使う
    float distanceX = GetComponent().Let((it) =>
    {
    return it.parent.transform.localPosition.x + it.localPosition.x;
    });

    View full-size slide

  27. Letを使う
    ● LINQやRxのSelectと同じイメージで、Lambda内で変形を
    行える
    ● Alsoと同じく、一時変数でスコープを汚さない

    View full-size slide

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

    View full-size slide

  29. Letを使わない
    string say(string comment)
    {
    return comment != null ? $"I said: {comment}" : "";
    }

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  32. できなかったこと

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  37. Apply(実現できたら)
    public static T Apply(this T self, Action action)
    {
    self.action();
    return self;
    }
    自分自身を返す

    View full-size slide

  38. Applyを使えたら
    void Start()
    {
    Transform t2 = GetComponent().Apply(() =>
    {
    this.rotation = new Quaternion().Apply(() =>
    {
    x = 0;
    y = 0;
    z = 0;
    });
    });
    }
    this以外の事について言及する余地
    が無くなる

    View full-size slide

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

    View full-size slide

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

    View full-size slide