diff --git a/src/Avalonia.Base/Data/Core/BindingExpression.cs b/src/Avalonia.Base/Data/Core/BindingExpression.cs index ae884fd37f..a96f051452 100644 --- a/src/Avalonia.Base/Data/Core/BindingExpression.cs +++ b/src/Avalonia.Base/Data/Core/BindingExpression.cs @@ -330,7 +330,7 @@ internal partial class BindingExpression : UntypedBindingExpressionBase, IDescri } // Don't set the value if it's unchanged. - if (LeafNode.IsValueAlive && TypeUtilities.IdentityEquals(LeafNode.Value, value, type)) + if (TypeUtilities.IdentityEquals(LeafNode.Value, value, type)) return true; try diff --git a/src/Avalonia.Base/Data/Core/ExpressionNodes/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNodes/ExpressionNode.cs index 150502d67a..e8e6633ab7 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionNodes/ExpressionNode.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionNodes/ExpressionNode.cs @@ -12,7 +12,7 @@ namespace Avalonia.Data.Core.ExpressionNodes; internal abstract class ExpressionNode { private WeakReference? _source; - private WeakReference? _value; + private object? _value = AvaloniaProperty.UnsetValue; /// /// Gets the index of the node in the binding path. @@ -40,29 +40,7 @@ internal abstract class ExpressionNode /// /// Gets the current value of the node. /// - public object? Value - { - get - { - if (_value is null) - return AvaloniaProperty.UnsetValue; - _value.TryGetTarget(out var value); - return value; - } - } - - /// - /// Gets a value indicating whether the node's is alive, i.e. - /// initialized and not a GC'd object. - /// - public bool IsValueAlive - { - get - { - return _value == UntypedBindingExpressionBase._nullReference || - _value?.TryGetTarget(out _) == true; - } - } + public object? Value => _value; /// /// Appends a string representation of the expression node to a string builder. @@ -88,7 +66,8 @@ internal abstract class ExpressionNode public void Reset() { SetSource(null, null); - _source = _value = null; + _source = null; + _value = AvaloniaProperty.UnsetValue; } /// @@ -140,7 +119,7 @@ internal abstract class ExpressionNode // If the source is null then the value is null. We explicitly do not want to call // OnSourceChanged as we don't want to raise errors for subsequent nodes in the // binding change. - _value = BindingExpression._nullReference; + _value = AvaloniaProperty.UnsetValue; } else { @@ -172,7 +151,7 @@ internal abstract class ExpressionNode /// The error message. protected void SetError(string message) { - _value = new(AvaloniaProperty.UnsetValue); + _value = AvaloniaProperty.UnsetValue; Owner?.OnNodeError(Index, message); } @@ -245,15 +224,13 @@ internal abstract class ExpressionNode // - This is the initial value (_value is null) // - There is a data validation error // - There is no data validation error, but the owner has one - // - The old value has been GC'd - in this case we don't know if the new value is different // - The new value is different to the old value if (_value is null || dataValidationError is not null || (dataValidationError is null && Owner.ErrorType == BindingErrorType.DataValidationError) || - _value.TryGetTarget(out var oldValue) == false || - !Equals(oldValue, value)) + !Equals(value, _value)) { - _value = value is null ? BindingExpression._nullReference : new(value); + _value = value; Owner.OnNodeValueChanged(Index, value, dataValidationError); } } diff --git a/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs b/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs index 2ba184a05f..e47a8e18af 100644 --- a/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs +++ b/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs @@ -21,7 +21,6 @@ public abstract class UntypedBindingExpressionBase : BindingExpressionBase, IValueEntry { protected static readonly object UnchangedValue = new(); - internal static readonly WeakReference _nullReference = new(null); private readonly bool _isDataValidationEnabled; private object? _defaultValue; private BindingError? _error; @@ -30,7 +29,7 @@ public abstract class UntypedBindingExpressionBase : BindingExpressionBase, private bool _produceValue; private IBindingExpressionSink? _sink; private WeakReference? _target; - private WeakReference? _value; + private object? _value = AvaloniaProperty.UnsetValue; /// /// Initializes a new instance of the class. @@ -92,7 +91,7 @@ public abstract class UntypedBindingExpressionBase : BindingExpressionBase, get { Start(produceValue: false); - return _value is not null; + return true; } } @@ -127,14 +126,7 @@ public abstract class UntypedBindingExpressionBase : BindingExpressionBase, { if (!IsRunning) throw new InvalidOperationException("BindingExpression has not been started."); - if (_value is null) - return AvaloniaProperty.UnsetValue; - else if (_value == _nullReference) - return null; - else if (_value.TryGetTarget(out var value)) - return value; - else - return AvaloniaProperty.UnsetValue; + return _value; } /// @@ -441,7 +433,7 @@ public abstract class UntypedBindingExpressionBase : BindingExpressionBase, var hasErrorChanged = error is not null || _error is not null; if (hasValueChanged) - _value = value is null ? _nullReference : new(value); + _value = value; _error = error; if (!_produceValue || _sink is null) @@ -516,7 +508,7 @@ public abstract class UntypedBindingExpressionBase : BindingExpressionBase, { StopCore(); _isRunning = false; - _value = null; + _value = AvaloniaProperty.UnsetValue; } /// @@ -566,7 +558,7 @@ public abstract class UntypedBindingExpressionBase : BindingExpressionBase, IAvaloniaSubject { private readonly UntypedBindingExpressionBase _expression; - private WeakReference? _value; + private object? _value = AvaloniaProperty.UnsetValue; public ObservableSink(UntypedBindingExpressionBase expression) => _expression = expression; @@ -607,18 +599,13 @@ public abstract class UntypedBindingExpressionBase : BindingExpressionBase, protected override void Subscribed(IObserver observer, bool first) { - if (!first && _value is not null) - { - if (_value == _nullReference) - base.PublishNext(null); - else if (_value.TryGetTarget(out var value)) - base.PublishNext(value); - } + if (!first && _value != AvaloniaProperty.UnsetValue) + base.PublishNext(_value); } private new void PublishNext(object? value) { - _value = (value is null) ? _nullReference : new(value); + _value = value; base.PublishNext(value); } }