Skip to content

Commit 2206ed7

Browse files
committed
FIX: GetOrSetValueAsync wasn't working when the backing field was inherited and private.
IMP: added another method to format numeric values (UnitFormatter.FormatValue), which should be faster and offers more flexibility.
1 parent 6085008 commit 2206ed7

File tree

2 files changed

+48
-9
lines changed

2 files changed

+48
-9
lines changed

Wokhan.Core/ComponentModel/Extensions/NotifyPropertyChangedExtensions.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public static void SetValue<T>(this INotifyPropertyChanged src, ref T field, T v
5858
/// <param name="fieldName">For internal use (for reflection, based on the 'targetField' parameter)</param>
5959
/// <param name="propertyName">For internal use (for reflection, based on the 'targetField' parameter)</param>
6060
/// <returns></returns>
61-
public unsafe static T? GetOrSetValueAsync<T>(this INotifyPropertyChanged src, Func<Task<T>> resolveAsync, ref T targetField, Action<string>? propertyChanged = null, [CallerArgumentExpression(nameof(targetField))] string? fieldName = null, [CallerMemberName] string? propertyName = null)
61+
public static T? GetOrSetValueAsync<T>(INotifyPropertyChanged src, Func<Task<T>> resolveAsync, ref T targetField, Action<string>? propertyChanged = null, [CallerArgumentExpression(nameof(targetField))] string? fieldName = null, [CallerMemberName] string? propertyName = null)
6262
{
6363
// Dangerous code as it doesn't ensure field has been pinned in memory, resulting in memory violations.
6464
// Keeping it in case someone has an idea.
@@ -77,18 +77,30 @@ public static void SetValue<T>(this INotifyPropertyChanged src, ref T field, T v
7777

7878
//return field;
7979

80-
81-
fieldName ??= $"<{propertyName}>k_BackingField"; // TODO: check how this is generated. Could be broken with some .NET future implementations.
82-
var fieldInfo = src.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
83-
if (fieldInfo is null)
84-
{
85-
throw new ArgumentOutOfRangeException(fieldName);
86-
}
80+
// Only compute the value if the target (backing) field is null
8781
if (targetField is null)
8882
{
83+
// TODO: check how this is generated. Could be broken with some .NET future implementations.
84+
fieldName ??= $"<{propertyName}>k_BackingField";
85+
86+
FieldInfo? fieldInfo = null;
87+
var type = src.GetType();
88+
// Loop over all inherited types if the property cannot be found on the current one
89+
while (fieldInfo is null && type is not null)
90+
{
91+
fieldInfo = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
92+
type = type.BaseType;
93+
}
94+
95+
if (fieldInfo is null)
96+
{
97+
throw new ArgumentOutOfRangeException(fieldName);
98+
}
99+
89100
//TODO: check why we are using TaskScheduler.Current and not Default (might cause a thread issue)
90101
_ = resolveAsync().ContinueWith(task => { fieldInfo.SetValue(src, task.Result); propertyChanged?.Invoke(propertyName!); }, TaskScheduler.Current);
91102
}
103+
92104
return targetField;
93105
}
94106
}

Wokhan.Core/Core/UnitFormatter.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,37 @@
11
using System;
22

3-
namespace Wokhan.Core.Core;
3+
namespace Wokhan.Core;
44

5+
/// <summary>
6+
/// A static class offering a single method to help formatting numeric values.
7+
/// </summary>
58
public static class UnitFormatter
69
{
10+
11+
private static string[] defaultUnits = new[] { "", "K", "M", "G", "T" };
12+
13+
/// <summary>
14+
/// Formats a given value (size, bandwitdth, ...) using the default units (["", "K", "M", "G", "T"]) or the specified one.
15+
/// For instance FormatValue(1_000_000, "bps") will return "1Mbps" and FormatValue(2_345_000, units: ["", "KiB", "MiB", "GiB", "TiB"]) will return "2.345MiB".
16+
/// </summary>
17+
/// <param name="value"></param>
18+
/// <param name="suffix">An optional suffix which will get added to the output string (for instance, 'bps').</param>
19+
/// <param name="mathBase">An optional mathematical base (1000, 1024) depending on the unit you'll use.</param>
20+
/// <param name="units">An array containing the units (related to the base you use).</param>
21+
/// <returns></returns>
22+
public static string FormatValue(double value, string? suffix = null, int mathBase = 1000, string[]? units = null)
23+
{
24+
units ??= defaultUnits;
25+
26+
var num = Math.Max(0, Math.Min(units.Length - 1, (int)Math.Log(value, mathBase)));
27+
value = (int)(value / Math.Pow(mathBase, num) * 100) / 100;
28+
29+
return $"{value:#0.##}{units[num]}{suffix}";
30+
}
31+
732
static string[] units = new[] { "B", "KiB", "MiB", "GiB", "TiB" };
33+
34+
[Obsolete("This method has been deprecated. Please use FormatUnit instead.")]
835
public static string FormatBytes(double size, string? suffix = null)
936
{
1037
int i;

0 commit comments

Comments
 (0)