MRが楽しい

MRやVRについて学習したことを書き残す

UniRxのReactivePropertyでClass,Struct,Recordでの差分チェックの動作を確認する

本日は UniRx の小ネタ枠です。
UniRx の ReactiveProperty でそれぞれ Class, Struct, Record での差分チェックの動作が分からず確認したので記事に残します。

ReactivePropertyの値の変更

UniRx の ReactiveProperty を使うと値の変更を検知して特定の処理を行うといったコードを簡単に記述することができます。
今回はの「値の変更」の検知条件について Class, Struct, Record での違いを確認しました。

以下のサンプルコードを作成しました。
bool 型の変数を持たせた各定義の値を代入しなおしたとき、bool 値が変化した場合のみ通知が発生するか否かを検証します。
・ReactivePropertyTest

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;

public class ReactivePropertyTest : MonoBehaviour
{
    /// <summary>
    /// Class
    /// </summary>
    private class CheckClass
    {
        public bool onoff;
    }

    /// <summary>
    /// Struct
    /// </summary>
    private struct CheckStruct
    {
        public bool onoff;
    }

    /// <summary>
    /// Record
    /// </summary>
    private record CheckRecord
    {
        public bool onoff;
    }

    // 各ReactiveProperty変数
    private ReactiveProperty<CheckClass> checkClassProp = new ReactiveProperty<CheckClass>(new CheckClass());
    private ReactiveProperty<CheckStruct> checkStructProp = new ReactiveProperty<CheckStruct>(new CheckStruct());
    private ReactiveProperty<CheckRecord> checkRecordProp = new ReactiveProperty<CheckRecord>(new CheckRecord());

    void Start()
        
    {
        // Class 変更検知時の処理
        checkClassProp
            .Subscribe(checkstate => { Debug.Log($"CheckClass Changed! : {checkstate.onoff}"); })
            .AddTo(this);

        // Struct 変更検知時の処理
        checkStructProp
            .Subscribe(checkstate => { Debug.Log($"CheckStruct Changed! : {checkstate.onoff}"); })
            .AddTo(this);

        // Record 変更検知時の処理
        checkRecordProp
            .Subscribe(checkstate => { Debug.Log($"CheckRecord Changed! : {checkstate.onoff}"); })
            .AddTo(this);
    }

    /// <summary>
    /// 全ての bool を True に変更
    /// </summary>
    [ContextMenu("ON")]
    void ChangeOn()
    {
        ChangeStates(true);
    }

    /// <summary>
    /// 全ての bool を False に変更
    /// </summary>
    [ContextMenu("OFF")]
    void ChangeOff()
    {
        ChangeStates(false);
    }

    void ChangeStates(bool isOnOff)
    {
        var checkClass = new CheckClass() { onoff = isOnOff };
        checkClassProp.Value = checkClass;

        var checkStruct = new CheckStruct() { onoff = isOnOff };
        checkStructProp.Value = checkStruct;

        var checkRecord = new CheckRecord() { onoff = isOnOff };
        checkRecordProp.Value = checkRecord;
    }
}

結果として以下のような動作となりました。
・false -> true への変化の場合、Class, Struct, Record 全てで通知が発生する
・false -> false への変化の場合、Class でのみ通知が発生する


仕組み

RectiveProperty のソースコードを確認すると値の変更チェックには Equals が利用されています。

Classの場合

Class の定義では Equeals の処理をオーバーライドしていない場合、参照先の比較が行われます。
false -> false の変更でも値を代入しなおしているため、参照先が変わっており、通知が発生しています。

Structの場合

Struct は値型のため、デフォルトで値の比較が行われます。
このため、false -> true の変更でのみ通知が発生しています。

Recordの場合

Record は参照型ですが、Equals の処理で値の比較が行われます。
このため、false -> true の変更でのみ通知が発生しています。
learn.microsoft.com