diff --git a/src/ReactiveMarbles.PropertyChanged/GetMemberFuncCache.cs b/src/ReactiveMarbles.PropertyChanged/GetMemberFuncCache.cs index 45328a7c..c8ec1274 100644 --- a/src/ReactiveMarbles.PropertyChanged/GetMemberFuncCache.cs +++ b/src/ReactiveMarbles.PropertyChanged/GetMemberFuncCache.cs @@ -12,25 +12,23 @@ namespace ReactiveMarbles.PropertyChanged internal static class GetMemberFuncCache { #if !UIKIT - private static readonly - ConcurrentDictionary<(Type FromType, string MemberName), Func> Cache - = new ConcurrentDictionary<(Type, string), Func>(); + private static readonly ConcurrentDictionary> Cache = new(new MemberFuncCacheKeyComparer()); #endif public static Func GetCache(MemberInfo memberInfo) { #if UIKIT - switch (memberInfo) - { - case PropertyInfo propertyInfo: - return input => (TReturn)propertyInfo.GetValue(input); - case FieldInfo fieldInfo: - return input => (TReturn)fieldInfo.GetValue(input); - default: - throw new ArgumentException($"Cannot handle member {memberInfo.Name}", nameof(memberInfo)); - } + switch (memberInfo) + { + case PropertyInfo propertyInfo: + return input => (TReturn)propertyInfo.GetValue(input); + case FieldInfo fieldInfo: + return input => (TReturn)fieldInfo.GetValue(input); + default: + throw new ArgumentException($"Cannot handle member {memberInfo.Name}", nameof(memberInfo)); + } #else - return Cache.GetOrAdd((memberInfo.DeclaringType, memberInfo.Name), _ => + return Cache.GetOrAdd(memberInfo, static memberInfo => { var instance = Expression.Parameter(typeof(TFrom), "instance"); diff --git a/src/ReactiveMarbles.PropertyChanged/MemberFuncCacheKeyComparer.cs b/src/ReactiveMarbles.PropertyChanged/MemberFuncCacheKeyComparer.cs new file mode 100644 index 00000000..fab4d8c2 --- /dev/null +++ b/src/ReactiveMarbles.PropertyChanged/MemberFuncCacheKeyComparer.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2019-2020 ReactiveUI Association Incorporated. All rights reserved. +// ReactiveUI Association Incorporated licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using System.Reflection; + +namespace ReactiveMarbles.PropertyChanged +{ + internal sealed class MemberFuncCacheKeyComparer : IEqualityComparer + { + public bool Equals(MemberInfo x, MemberInfo y) + { + return (x.DeclaringType, x.Name) == (y.DeclaringType, y.Name); + } + + public int GetHashCode(MemberInfo obj) + { + return (obj.DeclaringType, obj.Name).GetHashCode(); + } + } +} diff --git a/src/ReactiveMarbles.PropertyChanged/NotifyPropertyChangedExtensions.cs b/src/ReactiveMarbles.PropertyChanged/NotifyPropertyChangedExtensions.cs index ccf5fb9d..9993b491 100644 --- a/src/ReactiveMarbles.PropertyChanged/NotifyPropertyChangedExtensions.cs +++ b/src/ReactiveMarbles.PropertyChanged/NotifyPropertyChangedExtensions.cs @@ -74,17 +74,17 @@ public static IObservable WhenPropertyValueChanges( if (i == expressionChain.Count - 1) { - var function = GetMemberFuncCache.GetCache(memberInfo); - return currentObservable - .Where(parent => parent.Value != null) - .Select(parent => GenerateObservable(parent.Value, memberInfo, function)) + return Observable.Return(memberInfo) + .CombineLatest(currentObservable, (memberInfo, parent) => (memberInfo, sender: parent.Sender, value: parent.Value)) + .Where(x => x.value != null) + .Select(x => GenerateObservable(x.value, x.memberInfo, GetMemberFuncCache.GetCache(x.memberInfo))) .Switch(); } - var iFunction = GetMemberFuncCache.GetCache(memberInfo); - currentObservable = currentObservable - .Where(parent => parent.Value != null) - .Select(parent => GenerateObservable(parent.Value, memberInfo, iFunction)) + currentObservable = Observable.Return(memberInfo) + .CombineLatest(currentObservable, (memberInfo, parent) => (memberInfo, sender: parent.Sender, value: parent.Value)) + .Where(x => x.value != null) + .Select(x => GenerateObservable(x.value, x.memberInfo, GetMemberFuncCache.GetCache(x.memberInfo))) .Switch(); i++;