c# - Running an OnChange event for Properties of a Generic Class - Stack Overflow

admin2025-04-06  0

I've got this class that takes works with generics. I want to run an event any time you modify anything within T. Say you have a class called Settings with a few bools inside and pass that in as T. I want to call the DataVariable<> onChanged event any time any of the bools were modified but I'm not sure how or if it's even possible.

using System;
using UnityEngine;

namespace Udon {
    [Serializable]
    public class DataReference<T> {
        [SerializeField] private bool useConstant;
        [SerializeField] private DataVariable<T> variable;
        [SerializeField] private T constantValue;


        private T cachedValue;

        public T GetValue => useConstant ? constantValue : variable.value;

        public T SetValue {
            get {
                if (useConstant) return constantValue;
                variable.onChanged?.Raise();
                return variable.value;
            }
        }
    }
}

If possible this would mean turning the set and get values into one unified property called Value. How do I check if variables inside T were changed without knowing what T is? If there is a better way to do this I'd love to know too.

Full Codebase available at:

I've got this class that takes works with generics. I want to run an event any time you modify anything within T. Say you have a class called Settings with a few bools inside and pass that in as T. I want to call the DataVariable<> onChanged event any time any of the bools were modified but I'm not sure how or if it's even possible.

using System;
using UnityEngine;

namespace Udon {
    [Serializable]
    public class DataReference<T> {
        [SerializeField] private bool useConstant;
        [SerializeField] private DataVariable<T> variable;
        [SerializeField] private T constantValue;


        private T cachedValue;

        public T GetValue => useConstant ? constantValue : variable.value;

        public T SetValue {
            get {
                if (useConstant) return constantValue;
                variable.onChanged?.Raise();
                return variable.value;
            }
        }
    }
}

If possible this would mean turning the set and get values into one unified property called Value. How do I check if variables inside T were changed without knowing what T is? If there is a better way to do this I'd love to know too.

Full Codebase available at: https://gitlab/BitFl1p/udon

Share Improve this question asked Apr 1 at 20:36 BitFlipBitFlip 132 bronze badges 5
  • What is T? Most objects don't have that sort of change detection. For example, imagine it's a List<string>. That object doesn't raise any sort of event when an item is added or deleted. It has no way to notify another class that it has changed, which means nothing can respond. So that's the first step. T has to be something that raises events which something else can listen to. – Scott Hannen Commented Apr 1 at 22:48
  • As it stands, it's just anything unity can serialize. – BitFlip Commented Apr 1 at 23:12
  • Not possible if you want "anything unity can serialize", since you cannot determine when changes are made (see first comment, unity can serialize List<string>). You can implement your class variables so they do independent change tracking, but that doesn't seem to be what you're asking. – hijinxbassist Commented Apr 2 at 0:01
  • You do "change tracking"; which implies having a "before image" at the point you want to start tracking (without smart setters). You can iterate over an objects' properties using reflection; and compare same name properties on a similar object to detect changes. MemberwiseClone() uses reflection. Your access methods need to incorporate change tracking (e.g. "dirty"; not dirty) – Gerry Schmitz Commented Apr 2 at 0:10
  • You could go with R3 (former UniRX) and go for ReactivePropertys .. but currently it's a bit unclear where exactly you want to go with this .... – derHugo Commented Apr 2 at 16:02
Add a comment  | 

1 Answer 1

Reset to default 1

Enforce INotifyPropertyChanged on T

The cleanest way is to require T to implement INotifyPropertyChanged, which is a standard C# interface for notifying changes.

See bellow code

using System;
using System.ComponentModel;
using UnityEngine;

namespace Udon {
    [Serializable]
    public class DataReference<T> where T : INotifyPropertyChanged {
        [SerializeField] private bool useConstant;
        [SerializeField] private DataVariable<T> variable;
        [SerializeField] private T constantValue;

        private T cachedValue;

        public event Action OnValueChanged;

        public DataReference()
        {
            if (!useConstant && variable != null)
            {
                variable.value.PropertyChanged += HandlePropertyChanged;
            }
        }

        private void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            OnValueChanged?.Invoke();
        }

        public T Value
        {
            get => useConstant ? constantValue : variable.value;
            set
            {
                if (useConstant)
                {
                    constantValue = value;
                }
                else
                {
                    if (variable != null)
                    {
                        variable.value.PropertyChanged -= HandlePropertyChanged;
                        variable.value = value;
                        variable.value.PropertyChanged += HandlePropertyChanged;
                    }
                }

                OnValueChanged?.Invoke();
            }
        }
    }
}
转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1743872424a222175.html

最新回复(0)