Skip to content

Commit

Permalink
perf: Improve performance when reading dynamic properties.
Browse files Browse the repository at this point in the history
  • Loading branch information
jeanplevesque committed Jan 10, 2024
1 parent 80a56ae commit 7f68492
Showing 1 changed file with 138 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Chinook.DynamicMvvm;
using System.Xml.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
Expand Down Expand Up @@ -65,7 +67,40 @@ public static T Get<T>(
this IViewModel viewModel,
T initialValue = default,
[CallerMemberName] string name = null
) => viewModel.Get<T>(viewModel.GetOrCreateDynamicProperty(name, n => viewModel.GetDynamicPropertyFactory().Create(n, initialValue, viewModel)));
)
{
// We don't use GetOrCreateDynamicProperty internally to avoid the performance costs of the lambda and closure.
if (viewModel.IsDisposed)
{
return default(T);
}

if (viewModel.TryGetDisposable<IDynamicProperty>(name, out var property))
{
return viewModel.Get<T>(property);
}
else
{
property = AddDynamicPropertyFromValue(viewModel, initialValue, name);

return (T)property.Value;
}
}

private static IDynamicProperty AddDynamicPropertyFromValue<T>(IViewModel viewModel, T initialValue, string name)
{
var property = viewModel.GetDynamicPropertyFactory().Create(name, initialValue, viewModel);
property.ValueChanged += OnDynamicPropertyChanged;

viewModel.AddDisposable(name, property);

return property;

void OnDynamicPropertyChanged(IDynamicProperty dynamicProperty)
{
viewModel.RaisePropertyChanged(dynamicProperty.Name);
}
}

/// <summary>
/// Gets or creates a <see cref="IDynamicProperty"/> attached to this <see cref="IViewModel"/>.
Expand All @@ -79,7 +114,40 @@ public static T Get<T>(
this IViewModel viewModel,
Func<T> initialValue,
[CallerMemberName] string name = null
) => viewModel.Get<T>(viewModel.GetOrCreateDynamicProperty(name, n => viewModel.GetDynamicPropertyFactory().Create(n, initialValue(), viewModel)));
)
{
// We don't use GetOrCreateDynamicProperty internally to avoid the performance costs of the lambda and closure.
if (viewModel.IsDisposed)
{
return default(T);
}

if (viewModel.TryGetDisposable<IDynamicProperty>(name, out var property))
{
return viewModel.Get<T>(property);
}
else
{
property = AddDynamicPropertyFromValue(viewModel, initialValue, name);

return (T)property.Value;
}
}

private static IDynamicProperty AddDynamicPropertyFromValue<T>(IViewModel viewModel, Func<T> initialValue, string name)
{
var property = viewModel.GetDynamicPropertyFactory().Create(name, initialValue(), viewModel);
property.ValueChanged += OnDynamicPropertyChanged;

viewModel.AddDisposable(name, property);

return property;

void OnDynamicPropertyChanged(IDynamicProperty dynamicProperty)
{
viewModel.RaisePropertyChanged(dynamicProperty.Name);
}
}

/// <summary>
/// Gets or creates a <see cref="IDynamicProperty"/> attached to this <see cref="IViewModel"/>.
Expand All @@ -95,7 +163,40 @@ public static T GetFromTask<T>(
Func<CancellationToken, Task<T>> source,
T initialValue = default,
[CallerMemberName] string name = null
) => viewModel.Get<T>(viewModel.GetOrCreateDynamicProperty(name, n => viewModel.GetDynamicPropertyFactory().CreateFromTask(n, source, initialValue, viewModel)));
)
{
// We don't use GetOrCreateDynamicProperty internally to avoid the performance costs of the lambda and closure.
if (viewModel.IsDisposed)
{
return default(T);
}

if (viewModel.TryGetDisposable<IDynamicProperty>(name, out var property))
{
return viewModel.Get<T>(property);
}
else
{
property = AddDynamicPropertyTask(viewModel, source, initialValue, name);

return (T)property.Value;
}
}

private static IDynamicProperty AddDynamicPropertyTask<T>(IViewModel viewModel, Func<CancellationToken, Task<T>> source, T initialValue, string name)
{
var property = viewModel.GetDynamicPropertyFactory().CreateFromTask(name, source, initialValue, viewModel);
property.ValueChanged += OnDynamicPropertyChanged;

viewModel.AddDisposable(name, property);

return property;

void OnDynamicPropertyChanged(IDynamicProperty dynamicProperty)
{
viewModel.RaisePropertyChanged(dynamicProperty.Name);
}
}

/// <summary>
/// Gets or creates a <see cref="IDynamicProperty"/> attached to this <see cref="IViewModel"/>.
Expand All @@ -111,7 +212,40 @@ public static T GetFromObservable<T>(
IObservable<T> source,
T initialValue = default,
[CallerMemberName] string name = null
) => viewModel.Get<T>(viewModel.GetOrCreateDynamicProperty(name, n => viewModel.GetDynamicPropertyFactory().CreateFromObservable(n, source, initialValue, viewModel)));
)
{
// We don't use GetOrCreateDynamicProperty internally to avoid the performance costs of the lambda and closure.
if (viewModel.IsDisposed)
{
return default(T);
}

if (viewModel.TryGetDisposable<IDynamicProperty>(name, out var property))
{
return viewModel.Get<T>(property);
}
else
{
property = AddDynamicPropertyFromObservable(viewModel, source, initialValue, name);

return (T)property.Value;
}
}

private static IDynamicProperty AddDynamicPropertyFromObservable<T>(IViewModel viewModel, IObservable<T> source, T initialValue, string name)
{
var property = viewModel.GetDynamicPropertyFactory().CreateFromObservable(name, source, initialValue, viewModel);
property.ValueChanged += OnDynamicPropertyChanged;

viewModel.AddDisposable(name, property);

return property;

void OnDynamicPropertyChanged(IDynamicProperty dynamicProperty)
{
viewModel.RaisePropertyChanged(dynamicProperty.Name);
}
}

/// <summary>
/// Sets the value of a property.
Expand Down

0 comments on commit 7f68492

Please sign in to comment.