Skip to content

Commit 2823abe

Browse files
authored
Subscribe to fields instead of properties (#521)
1 parent 10fc0bd commit 2823abe

File tree

3 files changed

+33
-37
lines changed

3 files changed

+33
-37
lines changed

Docs/releases.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Releases
22

3+
## New in 6.5
4+
* Support subscribing to fields instead of properties ([#514](https://github.com/mrpmorris/Fluxor/issues/514))
5+
36
## New in 6.4
47
* Optimise Roslyn analyzer
58
* Use Microsoft.CodeAnalysis.CSharp instead of Microsoft.CodeAnalysis.CSharp.Workspaces

Source/Lib/Fluxor.UnitTests/StateSubscriberTests/SubscribeTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class SubscribeTests
1010

1111
private int CallbackInvocationCount;
1212
private string State = "X";
13-
private IStateSelection<string, char?> Selection { get; }
13+
private IStateSelection<string, char?> Selection;
1414

1515
[Fact]
1616
public void WhenSubscribedToSubject_ThenNotificationsShouldBeReceived()

Source/Lib/Fluxor/StateSubscriber.cs

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,23 @@
33
using System.Collections.Generic;
44
using System.Linq;
55
using System.Reflection;
6-
using GetStateChangedPropertyDelegate = System.Func<object, Fluxor.IStateChangedNotifier>;
6+
using GetStateChangedFieldDelegate = System.Func<object, Fluxor.IStateChangedNotifier>;
77

88
namespace Fluxor;
99

10-
/// <summary>
11-
/// A utility class that automatically subscribes to all <see cref="IStateChangedNotifier"/> properties
12-
/// on a specific object
13-
/// </summary>
1410
public static class StateSubscriber
1511
{
16-
private static readonly ConcurrentDictionary<Type, IEnumerable<GetStateChangedPropertyDelegate>> ValueDelegatesByType;
12+
private static readonly ConcurrentDictionary<Type, IEnumerable<GetStateChangedFieldDelegate>> ValueDelegatesByType;
1713

1814
static StateSubscriber()
19-
{
20-
ValueDelegatesByType = new ConcurrentDictionary<Type, IEnumerable<GetStateChangedPropertyDelegate>>();
21-
}
15+
=> ValueDelegatesByType = new ConcurrentDictionary<Type, IEnumerable<GetStateChangedFieldDelegate>>();
2216

2317
/// <summary>
24-
/// Subscribes to all <see cref="IStateChangedNotifier"/> properties on the specified <paramref name="subject"/>
25-
/// to ensure <paramref name="callback"/> is called whenever a state is modified
18+
/// Subscribes to all <see cref="IStateChangedNotifier"/> fields on the specified <paramref name="subject"/>
19+
/// to ensure <paramref name="callback"/> is called whenever a state is modified.
2620
/// </summary>
27-
/// <param name="subject">The object to scan for <see cref="IStateChangedNotifier"/> properties.</param>
28-
/// <param name="callback">The action to execute when one of the states are modified</param>
21+
/// <param name="subject">The object to scan for <see cref="IStateChangedNotifier"/> fields.</param>
22+
/// <param name="callback">The action to execute when one of the states are modified.</param>
2923
/// <returns></returns>
3024
public static IDisposable Subscribe(object subject, Action<IStateChangedNotifier> callback)
3125
{
@@ -34,14 +28,15 @@ public static IDisposable Subscribe(object subject, Action<IStateChangedNotifier
3428
if (callback is null)
3529
throw new ArgumentNullException(nameof(callback));
3630

37-
IEnumerable<GetStateChangedPropertyDelegate> getStateChangedNotifierPropertyDelegates =
38-
GetStateChangedNotifierPropertyDelegatesForType(subject.GetType());
31+
IEnumerable<GetStateChangedFieldDelegate> getStateChangedNotifierFieldDelegates =
32+
GetStateChangedNotifierFieldDelegatesForType(subject.GetType());
33+
3934
var subscriptions = new List<(IStateChangedNotifier StateChangedNotifier, EventHandler Handler)>();
40-
41-
foreach (GetStateChangedPropertyDelegate getStateChangedNotifierPropertyValue in getStateChangedNotifierPropertyDelegates)
35+
36+
foreach (GetStateChangedFieldDelegate getStateChangedNotifierFieldValue in getStateChangedNotifierFieldDelegates)
4237
{
43-
IStateChangedNotifier stateChangedNotifier = getStateChangedNotifierPropertyValue(subject);
44-
var handler = new EventHandler((s, a) => callback(stateChangedNotifier));
38+
IStateChangedNotifier stateChangedNotifier = getStateChangedNotifierFieldValue(subject);
39+
var handler = new EventHandler((_, __) => callback(stateChangedNotifier));
4540

4641
subscriptions.Add((stateChangedNotifier, handler));
4742
stateChangedNotifier.StateChanged += handler;
@@ -50,34 +45,32 @@ public static IDisposable Subscribe(object subject, Action<IStateChangedNotifier
5045
return new DisposableCallback(
5146
id: $"{nameof(StateSubscriber)}.{nameof(Subscribe)} / {subject.GetType().FullName}",
5247
() =>
53-
{
54-
foreach (var subscription in subscriptions)
55-
subscription.StateChangedNotifier.StateChanged -= subscription.Handler;
56-
});
48+
{
49+
foreach (var subscription in subscriptions)
50+
subscription.StateChangedNotifier.StateChanged -= subscription.Handler;
51+
});
5752
}
5853

59-
private static IEnumerable<PropertyInfo> GetStateChangedNotifierProperties(Type t) =>
54+
private static IEnumerable<FieldInfo> GetStateChangedNotifierFields(Type t) =>
6055
t == typeof(object)
61-
? Enumerable.Empty<PropertyInfo>()
62-
: GetStateChangedNotifierProperties(t.BaseType)
56+
? Enumerable.Empty<FieldInfo>()
57+
: GetStateChangedNotifierFields(t.BaseType)
6358
.Union(
64-
t.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)
65-
.Where(p => typeof(IStateChangedNotifier).IsAssignableFrom(p.PropertyType))
59+
t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)
60+
.Where(f => typeof(IStateChangedNotifier).IsAssignableFrom(f.FieldType))
6661
);
6762

68-
private static IEnumerable<GetStateChangedPropertyDelegate> GetStateChangedNotifierPropertyDelegatesForType(Type type)
63+
private static IEnumerable<GetStateChangedFieldDelegate> GetStateChangedNotifierFieldDelegatesForType(Type type)
6964
{
7065
return ValueDelegatesByType.GetOrAdd(type, _ =>
7166
{
72-
var delegates = new List<GetStateChangedPropertyDelegate>();
73-
IEnumerable<PropertyInfo> stateChangedNotifierProperties = GetStateChangedNotifierProperties(type);
67+
var delegates = new List<GetStateChangedFieldDelegate>();
68+
IEnumerable<FieldInfo> stateChangedNotifierFields = GetStateChangedNotifierFields(type);
7469

75-
foreach (PropertyInfo currentProperty in stateChangedNotifierProperties)
70+
foreach (FieldInfo currentField in stateChangedNotifierFields)
7671
{
77-
Type getterMethod = typeof(Func<,>).MakeGenericType(type, currentProperty.PropertyType);
78-
var stronglyTypedDelegate = Delegate.CreateDelegate(getterMethod, currentProperty.GetGetMethod(true));
79-
var getValueDelegate = new GetStateChangedPropertyDelegate(
80-
x => (IStateChangedNotifier)stronglyTypedDelegate.DynamicInvoke(x));
72+
var getValueDelegate = new GetStateChangedFieldDelegate(
73+
x => (IStateChangedNotifier)currentField.GetValue(x)!);
8174
delegates.Add(getValueDelegate);
8275
}
8376

0 commit comments

Comments
 (0)