ルーグの備忘録

主にC#についてまとめてます。

クロージャに甘えない

クロージャとは

ラムダ式とかで関数の外にある変数や関数を使うと、実はそれらをキャプチャするために暗黙的にクラスがnewされる。この仕組みを使っている関数をクロージャと呼ぶ。引数を渡さなくても自由にラムダ式を書けるのはこの便利な仕組みのおかげ。
日本語だと関数閉包っていうらしい。

クロージャは甘え?

クロージャで暗黙的にnewされるクラスはしっかりGCゴミになるので、なるたけクロージャは避けた方がいい。

対策例

UniRxではステート付き関数が充実しているのでそれを使えば良い。

SubscribeWithState

    protected override void Initialize()
    {
        base.Initialize();

        // 入力購読
        m_InputManager.InputEvent.SubscribeWithState(this, (input, self) => self.DetectInput(input.KeyCodeFlag)).AddTo(this);
    }

SubscribeWithState2

    /// <summary>
    /// 敵ステータスセット
    /// </summary>
    /// <param name="enemyStatus"></param>
    private void SetEnemyStatus(EnemyStatus enemyStatus)
    {
        ..... 前略

        // 死亡時に経験値を与える
        if (Owner.RequireEvent<ICharaBattleEvent>(out var battle) == true)
        {
            battle.OnDead.SubscribeWithState2(this, enemyStatus, (result, self, enemyStatus) =>
            {
                foreach (var unit in self.m_UnitHolder.FriendList)
                {
                    // 味方キャラによるキルなら経験値加算
                    if (result.Attacker == unit)
                    {
                        self.m_TeamLevelHandler.AddExperience(enemyStatus.Param.Ex);
                        break;
                    }
                }
            }).AddTo(CompositeDisposable);
        }
    }

Disposable.CreateWithState

2つ目の例ではタプルを使って複数の値を引き渡している。

    /// <summary>
    /// カメラを追従させる
    /// </summary>
    /// <param name="parent"></param>
    /// <returns></returns>
    IDisposable ICameraHandler.SetParent(GameObject parent)
    {
        m_MainCamera.transform.SetParent(parent.transform);
        return Disposable.CreateWithState(this, self => self.m_MainCamera.transform.parent = null);
    }
    /// <summary>
    /// バフを付与する
    /// </summary>
    /// <param name="buff"></param>
    /// <returns></returns>
    public IDisposable AddBuff(BuffTicket buff)
    {
        m_BuffList.Add(buff);
        return Disposable.CreateWithState((m_BuffList, buff), tuple => tuple.m_BuffList.Remove(tuple.buff));
    }

参考

UniRx作者さんの記事。
neue.cc
neue.cc