From 1e4577fa3f0fbcd6940b382fdfa9521131ea9ecc Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 19 Jul 2022 16:28:00 +0200 Subject: [PATCH] Don't produce value when reading (Has)Value. --- .../PropertyStore/BindingEntry.cs | 37 +++++++------- .../PropertyStore/BindingEntry`1.cs | 51 +++++++++---------- .../PropertyStore/UntypedBindingEntry.cs | 33 ++++++------ 3 files changed, 59 insertions(+), 62 deletions(-) diff --git a/src/Avalonia.Base/PropertyStore/BindingEntry.cs b/src/Avalonia.Base/PropertyStore/BindingEntry.cs index d95cd5ed05..e3240952cb 100644 --- a/src/Avalonia.Base/PropertyStore/BindingEntry.cs +++ b/src/Avalonia.Base/PropertyStore/BindingEntry.cs @@ -29,7 +29,7 @@ namespace Avalonia.PropertyStore { get { - StartIfNecessary(); + Start(produceValue: false); return _hasValue; } } @@ -44,7 +44,7 @@ namespace Avalonia.PropertyStore public object? GetValue() { - StartIfNecessary(); + Start(produceValue: false); if (!_hasValue) throw new AvaloniaInternalException("The binding entry has no value."); return _value!; @@ -52,21 +52,12 @@ namespace Avalonia.PropertyStore public bool TryGetValue(out object? value) { - StartIfNecessary(); + Start(produceValue: false); value = _value; return _hasValue; } - public void Start() - { - Debug.Assert(_subscription is null); - - // Subscription won't be set until Subscribe completes, but in the meantime we - // need to signal that we've started as Subscribe may cause a value to be produced. - _subscription = Disposable.Empty; - _subscription = _source.Subscribe(this); - } - + public void Start() => Start(true); public void OnCompleted() => BindingCompleted(); public void OnError(Exception error) => BindingCompleted(); @@ -84,7 +75,9 @@ namespace Avalonia.PropertyStore { _hasValue = false; _value = default; - _frame.Owner?.OnBindingValueCleared(Property, _frame.Priority); + + if (_subscription is not null) + _frame.Owner?.OnBindingValueCleared(Property, _frame.Priority); } } @@ -112,7 +105,9 @@ namespace Avalonia.PropertyStore { _value = typedValue; _hasValue = true; - _frame.Owner?.OnBindingValueChanged(Property, _frame.Priority, typedValue); + + if (_subscription is not null) + _frame.Owner?.OnBindingValueChanged(Property, _frame.Priority, typedValue); } } else @@ -128,10 +123,16 @@ namespace Avalonia.PropertyStore _frame.OnBindingCompleted(this); } - private void StartIfNecessary() + private void Start(bool produceValue) { - if (_subscription is null) - Start(); + if (_subscription is not null) + return; + + // Will only produce a new value when subscription isn't null. + if (produceValue) + _subscription = Disposable.Empty; + + _subscription = _source.Subscribe(this); } } } diff --git a/src/Avalonia.Base/PropertyStore/BindingEntry`1.cs b/src/Avalonia.Base/PropertyStore/BindingEntry`1.cs index 38b1e42228..8614492c27 100644 --- a/src/Avalonia.Base/PropertyStore/BindingEntry`1.cs +++ b/src/Avalonia.Base/PropertyStore/BindingEntry`1.cs @@ -42,7 +42,7 @@ namespace Avalonia.PropertyStore { get { - StartIfNecessary(); + Start(produceValue: false); return _hasValue; } } @@ -58,31 +58,17 @@ namespace Avalonia.PropertyStore public T GetValue() { - StartIfNecessary(); + Start(produceValue: false); if (!_hasValue) throw new AvaloniaInternalException("The binding entry has no value."); return _value!; } - public void Start() - { - Debug.Assert(_subscription is null); - - // Subscription won't be set until Subscribe completes, but in the meantime we - // need to signal that we've started as Subscribe may cause a value to be produced. - _subscription = Disposable.Empty; - - if (_source is IObservable> bv) - _subscription = bv.Subscribe(this); - else if (_source is IObservable b) - _subscription = b.Subscribe(this); - else - throw new AvaloniaInternalException("Unexpected binding source."); - } + public void Start() => Start(true); public bool TryGetValue([MaybeNullWhen(false)] out T value) { - StartIfNecessary(); + Start(produceValue: false); value = _value; return _hasValue; } @@ -111,7 +97,7 @@ namespace Avalonia.PropertyStore object? IValueEntry.GetValue() { - StartIfNecessary(); + Start(produceValue: false); if (!_hasValue) throw new AvaloniaInternalException("The BindingEntry has no value."); return _value!; @@ -119,7 +105,7 @@ namespace Avalonia.PropertyStore bool IValueEntry.TryGetValue(out object? value) { - StartIfNecessary(); + Start(produceValue: false); value = _value; return _hasValue; } @@ -130,7 +116,8 @@ namespace Avalonia.PropertyStore { _hasValue = false; _value = default; - _frame.Owner?.OnBindingValueCleared(Property, _frame.Priority); + if (_subscription is not null) + _frame.Owner?.OnBindingValueCleared(Property, _frame.Priority); } } @@ -145,10 +132,11 @@ namespace Avalonia.PropertyStore { _value = value; _hasValue = true; - _frame.Owner?.OnBindingValueChanged(Property, _frame.Priority, value); + if (_subscription is not null) + _frame.Owner?.OnBindingValueChanged(Property, _frame.Priority, value); } } - else + else if (_subscription is not null) { _frame.Owner?.OnBindingValueCleared(Property, _frame.Priority); } @@ -160,10 +148,21 @@ namespace Avalonia.PropertyStore _frame.OnBindingCompleted(this); } - private void StartIfNecessary() + private void Start(bool produceValue) { - if (_subscription is null) - Start(); + if (_subscription is not null) + return; + + // Will only produce a new value when subscription isn't null. + if (produceValue) + _subscription = Disposable.Empty; + + _subscription = _source switch + { + IObservable> bv => bv.Subscribe(this), + IObservable b => b.Subscribe(this), + _ => throw new AvaloniaInternalException("Unexpected binding source."), + }; } } } diff --git a/src/Avalonia.Base/PropertyStore/UntypedBindingEntry.cs b/src/Avalonia.Base/PropertyStore/UntypedBindingEntry.cs index c95c6444d0..890b4424fb 100644 --- a/src/Avalonia.Base/PropertyStore/UntypedBindingEntry.cs +++ b/src/Avalonia.Base/PropertyStore/UntypedBindingEntry.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reactive.Disposables; using Avalonia.Data; @@ -31,7 +30,7 @@ namespace Avalonia.PropertyStore { get { - StartIfNecessary(); + Start(produceValue: false); return _hasValue; } } @@ -47,25 +46,17 @@ namespace Avalonia.PropertyStore public T GetValue() { - StartIfNecessary(); + Start(produceValue: false); if (!_hasValue) throw new AvaloniaInternalException("The binding entry has no value."); return _value!; } - public void Start() - { - Debug.Assert(_subscription is null); - - // Subscription won't be set until Subscribe completes, but in the meantime we - // need to signal that we've started as Subscribe may cause a value to be produced. - _subscription = Disposable.Empty; - _subscription = _source.Subscribe(this); - } + public void Start() => Start(true); public bool TryGetValue([MaybeNullWhen(false)] out T value) { - StartIfNecessary(); + Start(produceValue: false); value = _value; return _hasValue; } @@ -91,7 +82,7 @@ namespace Avalonia.PropertyStore object? IValueEntry.GetValue() { - StartIfNecessary(); + Start(produceValue: false); if (!_hasValue) throw new AvaloniaInternalException("The BindingEntry has no value."); return _value!; @@ -99,7 +90,7 @@ namespace Avalonia.PropertyStore bool IValueEntry.TryGetValue(out object? value) { - StartIfNecessary(); + Start(produceValue: false); value = _value; return _hasValue; } @@ -154,10 +145,16 @@ namespace Avalonia.PropertyStore _frame.OnBindingCompleted(this); } - private void StartIfNecessary() + private void Start(bool produceValue) { - if (_subscription is null) - Start(); + if (_subscription is not null) + return; + + // Will only produce a new value when subscription isn't null. + if (produceValue) + _subscription = Disposable.Empty; + + _subscription = _source.Subscribe(this); } } }