Created
September 15, 2023 17:46
-
-
Save 0xF6/31d2cb03333c9a5e22de8d2a9415ef8d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal interface IObserverLinkedList<T> | |
{ | |
void UnsubscribeNode(ObserverNode<T> node); | |
} | |
internal sealed class ObserverNode<T> : IObserver<T>, IDisposable | |
{ | |
private readonly IObserver<T> observer; | |
private IObserverLinkedList<T> list; | |
public ObserverNode<T> Previous { get; internal set; } | |
public ObserverNode<T> Next { get; internal set; } | |
public ObserverNode(IObserverLinkedList<T> list, IObserver<T> observer) => | |
(this.list, this.observer) = (list, observer); | |
public void OnNext(T value) => this.observer.OnNext(value); | |
public void OnError(Exception error) => this.observer.OnError(error); | |
public void OnCompleted() => this.observer.OnCompleted(); | |
public void Dispose() => Interlocked.Exchange(ref this.list, null)?.UnsubscribeNode(this); | |
} | |
public interface ISerializedPropertyAccessor | |
{ | |
bool HasDefaultValue { get; } | |
} | |
public interface ISerializedPropertyAccessor<in T> : ISerializedPropertyAccessor | |
{ | |
void SetValue(T value); | |
} | |
public class SerializedProperty<T> : IReactiveProperty<T>, | |
IReadOnlyReactiveProperty<T>, | |
IObservable<T>, | |
IDisposable, | |
IOptimizedObservable<T>, | |
IObserverLinkedList<T>, | |
ISerializedPropertyAccessor<T> | |
{ | |
private T value; | |
private ObserverNode<T> root; | |
private ObserverNode<T> last; | |
private bool isDisposed; | |
public T Value | |
{ | |
get => this.value; | |
set | |
{ | |
if (this.GetEqualityComparer().Equals(this.value, value)) | |
return; | |
this.SetValue(value); | |
if (this.isDisposed) | |
return; | |
this.RaiseOnNext(ref value); | |
} | |
} | |
public bool HasValue => true; | |
public bool HasDefaultValue { get; private set; } = true; | |
public SerializedProperty() | |
: this(default(T)) => | |
this.shadowProperty = new ShadowProperty<T>(); | |
public SerializedProperty(T initialValue) => | |
this.SetValue(initialValue, true); | |
private void RaiseOnNext(ref T v) | |
{ | |
for (ObserverNode<T> observerNode = this.root; observerNode != null; observerNode = observerNode.Next) | |
observerNode.OnNext(v); | |
} | |
protected void SetValue(T v, bool isInitial = false) | |
{ | |
if (!isInitial) HasDefaultValue = false; | |
this.value = v; | |
} | |
public void SetValueAndForceNotify(T v) | |
{ | |
this.SetValue(v); | |
if (this.isDisposed) | |
return; | |
this.RaiseOnNext(ref v); | |
} | |
public IDisposable Subscribe(IObserver<T> observer) | |
{ | |
if (this.isDisposed) | |
{ | |
observer.OnCompleted(); | |
return Disposable.Empty; | |
} | |
observer.OnNext(this.value); | |
ObserverNode<T> observerNode = new ObserverNode<T>(this, observer); | |
if (this.root == null) | |
this.root = this.last = observerNode; | |
else | |
{ | |
this.last.Next = observerNode; | |
observerNode.Previous = this.last; | |
this.last = observerNode; | |
} | |
return observerNode; | |
} | |
void IObserverLinkedList<T>.UnsubscribeNode(ObserverNode<T> node) | |
{ | |
if (node == this.root) | |
this.root = node.Next; | |
if (node == this.last) | |
this.last = node.Previous; | |
if (node.Previous != null) | |
node.Previous.Next = node.Next; | |
if (node.Next == null) | |
return; | |
node.Next.Previous = node.Previous; | |
} | |
public void Dispose() | |
{ | |
shadowProperty.Dispose(); | |
this.Dispose(true); | |
GC.SuppressFinalize((object)this); | |
} | |
protected virtual void Dispose(bool disposing) | |
{ | |
if (this.isDisposed) | |
return; | |
ObserverNode<T> observerNode = this.root; | |
this.root = this.last = (ObserverNode<T>)null; | |
this.isDisposed = true; | |
for (; observerNode != null; observerNode = observerNode.Next) | |
observerNode.OnCompleted(); | |
} | |
void ISerializedPropertyAccessor<T>.SetValue(T v) => SetValue(v, true); | |
public override string ToString() => (object)this.value != null ? this.value.ToString() : "(null)"; | |
public bool IsRequiredSubscribeOnCurrentThread() => false; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment