Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 28 additions & 58 deletions src/Controls/src/Core/BindableObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Logging;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Dispatching;
Expand Down Expand Up @@ -38,8 +39,8 @@ public BindableObject()
}

internal ushort _triggerCount = 0;
internal Dictionary<TriggerBase, SetterSpecificity> _triggerSpecificity = new Dictionary<TriggerBase, SetterSpecificity>();
readonly Dictionary<BindableProperty, BindablePropertyContext> _properties = new Dictionary<BindableProperty, BindablePropertyContext>(4);
internal Dictionary<TriggerBase, SetterSpecificity> _triggerSpecificity = new();
readonly Dictionary<int, BindablePropertyContext> _properties = new(4);
bool _applying;
WeakReference _inheritedContext;

Expand Down Expand Up @@ -172,66 +173,19 @@ public object GetValue(BindableProperty property)
return context == null ? property.DefaultValue : context.Values.GetValue();
}

internal LocalValueEnumerator GetLocalValueEnumerator() => new LocalValueEnumerator(this);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed unused methods/classes


internal sealed class LocalValueEnumerator : IEnumerator<LocalValueEntry>
{
Dictionary<BindableProperty, BindablePropertyContext>.Enumerator _propertiesEnumerator;
internal LocalValueEnumerator(BindableObject bindableObject) => _propertiesEnumerator = bindableObject._properties.GetEnumerator();

object IEnumerator.Current => Current;
public LocalValueEntry Current { get; private set; }

public bool MoveNext()
{
if (_propertiesEnumerator.MoveNext())
{
Current = new LocalValueEntry(_propertiesEnumerator.Current.Key, _propertiesEnumerator.Current.Value.Values.GetValue(), _propertiesEnumerator.Current.Value.Attributes);
return true;
}
return false;
}

public void Dispose() => _propertiesEnumerator.Dispose();

void IEnumerator.Reset()
{
((IEnumerator)_propertiesEnumerator).Reset();
Current = null;
}
}

internal sealed class LocalValueEntry
{
internal LocalValueEntry(BindableProperty property, object value, BindableContextAttributes attributes)
{
Property = property;
Value = value;
Attributes = attributes;
}

public BindableProperty Property { get; }
public object Value { get; }
public BindableContextAttributes Attributes { get; }
}

internal (bool IsSet, T Value)[] GetValues<T>(BindableProperty[] propArray)
{
Dictionary<BindableProperty, BindablePropertyContext> properties = _properties;
var properties = _properties;
var resultArray = new (bool IsSet, T Value)[propArray.Length];

for (int i = 0; i < propArray.Length; i++)
{
if (properties.TryGetValue(propArray[i], out var context))
ref var result = ref resultArray[i];
if (properties.TryGetValue(propArray[i].InternalId, out var context))
{
var pair = context.Values.GetSpecificityAndValue();
resultArray[i].IsSet = pair.Key != SetterSpecificity.DefaultValue;
resultArray[i].Value = (T)pair.Value;
}
else
{
resultArray[i].IsSet = false;
resultArray[i].Value = default(T);
result.IsSet = pair.Key != SetterSpecificity.DefaultValue;
result.Value = (T)pair.Value;
}
}

Expand Down Expand Up @@ -736,7 +690,7 @@ static void BindingContextPropertyChanged(BindableObject bindable, object oldval
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
BindablePropertyContext CreateAndAddContext(BindableProperty property)
BindablePropertyContext CreateContext(BindableProperty property)
{
var defaultValueCreator = property.DefaultValueCreator;
var context = new BindablePropertyContext { Property = property };
Expand All @@ -745,15 +699,31 @@ BindablePropertyContext CreateAndAddContext(BindableProperty property)
if (defaultValueCreator != null)
context.Attributes = BindableContextAttributes.IsDefaultValueCreated;

_properties.Add(property, context);
return context;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal BindablePropertyContext GetContext(BindableProperty property) => _properties.TryGetValue(property, out var result) ? result : null;
internal BindablePropertyContext GetContext(BindableProperty property) => _properties.TryGetValue(property.InternalId, out var result) ? result : null;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
BindablePropertyContext GetOrCreateContext(BindableProperty property) => GetContext(property) ?? CreateAndAddContext(property);
BindablePropertyContext GetOrCreateContext(BindableProperty property)
{
#if NETSTANDARD
var context = GetContext(property);
if (context is null)
{
context = CreateContext(property);
_properties.Add(property.InternalId, context);
}
#else
ref var context = ref CollectionsMarshal.GetValueRefOrAddDefault(_properties, property.InternalId, out var exists);
if (!exists)
{
context = CreateContext(property);
}
#endif
return context;
}

void RemoveBinding(BindableProperty property, BindablePropertyContext context, SetterSpecificity specificity)
{
Expand Down
6 changes: 6 additions & 0 deletions src/Controls/src/Core/BindableProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Threading;
using Microsoft.Maui.Controls.Xaml;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Graphics.Converters;
Expand Down Expand Up @@ -181,6 +182,9 @@ public sealed class BindableProperty
/// <include file="../../docs/Microsoft.Maui.Controls/BindableProperty.xml" path="//Member[@MemberName='UnsetValue']/Docs/*" />
public static readonly object UnsetValue = new object();

private static int _nextInternalId = int.MinValue;
internal readonly int InternalId;

BindableProperty(string propertyName, [DynamicallyAccessedMembers(ReturnTypeMembers)] Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
CoerceValueDelegate coerceValue = null, BindablePropertyBindingChanging bindingChanging = null, bool isReadOnly = false, CreateDefaultValueDelegate defaultValueCreator = null)
Expand All @@ -191,6 +195,8 @@ public sealed class BindableProperty
throw new ArgumentNullException(nameof(returnType));
if (declaringType is null)
throw new ArgumentNullException(nameof(declaringType));

InternalId = Interlocked.Increment(ref _nextInternalId);

// don't use Enum.IsDefined as its redonkulously expensive for what it does
if (defaultBindingMode != BindingMode.Default && defaultBindingMode != BindingMode.OneWay && defaultBindingMode != BindingMode.OneWayToSource && defaultBindingMode != BindingMode.TwoWay && defaultBindingMode != BindingMode.OneTime)
Expand Down
38 changes: 38 additions & 0 deletions src/Core/tests/Benchmarks/Benchmarks/BindableObjectBenchmarker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Linq;
using BenchmarkDotNet.Attributes;
using Microsoft.Maui.Controls;

namespace Microsoft.Maui.Benchmarks
{
[MemoryDiagnoser]
public class BindableObjectBenchmarker
{
BindableProperty[] _properties;

[Params(1, 3, 8, 15, 30, 50)]
public int PropertiesToSet { get; set; }

[GlobalSetup]
public void Setup()
{
_properties = Enumerable.Range(0, PropertiesToSet)
.Select(i => BindableProperty.Create($"Property{i}", typeof(int), typeof(BindableObject), -1))
.ToArray();
}

private class Bindable : BindableObject {}

[Benchmark]
public void SetsAndReadsProperties()
{
var bindable = new Bindable();

var count = _properties.Length;
for (int i = 0; i < count; i++)
{
bindable.SetValue(_properties[i], i);
_ = bindable.GetValue(_properties[i]);
}
}
}
}
Loading