88//
99// ********************************************************************
1010
11+ // ReSharper disable CheckNamespace
12+
1113using System ;
1214using UnityEngine ;
1315
@@ -18,9 +20,13 @@ namespace RimuruDev
1820 public class ReactiveProperty < T >
1921 {
2022 [ SerializeField ] private T value ;
21-
2223 [ NonSerialized ] private Action < T > onChanged ;
2324
25+ public ReactiveProperty ( T initialValue = default )
26+ {
27+ value = initialValue ;
28+ }
29+
2430 public event Action < T > OnChanged
2531 {
2632 add
@@ -46,9 +52,51 @@ public void OnNext(T nextValue)
4652 Value = nextValue ;
4753 }
4854
49- public ReactiveProperty ( T initialValue = default )
55+ /// <summary>
56+ /// Подписка на изменения с поддержкой IDisposable.
57+ /// <example>
58+ /// <code>
59+ /// Пример:
60+ /// 1:
61+ /// private readonly List-IDisposable> subscriptions = new();
62+ ///
63+ /// 2:
64+ /// subscriptions.Add(UserName.Subscribe(value => Origin.UserName = value));
65+ ///
66+ /// 3:
67+ /// foreach (var subscription in subscriptions)
68+ /// subscription.Dispose();
69+ /// </code>
70+ /// </example>
71+ /// </summary>
72+ /// <param name="callback">Метод обратного вызова.</param>
73+ /// <returns>Объект IDisposable для отписки.</returns>
74+ public IDisposable Subscribe ( Action < T > callback )
5075 {
51- value = initialValue ;
76+ OnChanged += callback ;
77+
78+ // NOTE: Возвращаем объект, который при Dispose выполнит отписку.
79+ return new DisposableAction ( ( ) => OnChanged -= callback ) ;
80+ }
81+
82+ private class DisposableAction : IDisposable
83+ {
84+ private readonly Action disposeAction ;
85+ private bool isDisposed ;
86+
87+ public DisposableAction ( Action disposeAction )
88+ {
89+ this . disposeAction = disposeAction ;
90+ }
91+
92+ public void Dispose ( )
93+ {
94+ if ( ! isDisposed )
95+ {
96+ disposeAction ? . Invoke ( ) ;
97+ isDisposed = true ;
98+ }
99+ }
52100 }
53101 }
54102}
0 commit comments