Skip to content

Commit 870ec93

Browse files
use GetOrAdd in the cache dictionaries (#3)
* use GetOrAdd in Get * use GetOrAdd in set * use GetOrAdd in Get generic * rename Cache * lazy in MethodOf
1 parent ec7211f commit 870ec93

File tree

6 files changed

+65
-77
lines changed

6 files changed

+65
-77
lines changed

src/ExpressionCache.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ namespace MyNihongo.Expressions
1010
{
1111
internal static class ExpressionCache
1212
{
13-
internal static readonly ConcurrentDictionary<Tuple<Type, string>, Delegate>
13+
internal static readonly ConcurrentDictionary<Tuple<Type, string>, Lazy<Delegate>>
1414
PropertyGetters = new(),
1515
PropertySetters = new();
1616

17-
internal static readonly ConcurrentDictionary<Tuple<Type, string>, Delegate>
17+
internal static readonly ConcurrentDictionary<Tuple<Type, string>, Lazy<Delegate>>
1818
Invoke = new();
1919
}
2020
}

src/MethodOf.cs

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,36 +10,6 @@ public static class MethodOf
1010
{
1111
public static T? Invoke<T>(object @this, string methodName, params object[] args)
1212
{
13-
var type = @this.GetType();
14-
var key = new Tuple<Type, string>(type, methodName);
15-
16-
if (!ExpressionCache.Invoke.TryGetValue(key, out var lambda))
17-
{
18-
var param = Expression.Parameter(type);
19-
20-
if (args.Length == 0)
21-
{
22-
var call = Expression.Call(param, methodName, null);
23-
lambda = Expression.Lambda(call, param).Compile();
24-
}
25-
else
26-
{
27-
var @params = new Expression[args.Length];
28-
29-
for (var i = 0; i < args.Length; i++)
30-
@params[i] = Expression.Parameter(args[i].GetType());
31-
32-
var lambdaParams = new ParameterExpression[args.Length + 1];
33-
lambdaParams[0] = param;
34-
Array.Copy(@params, 0, lambdaParams, 1, @params.Length);
35-
36-
var call = Expression.Call(param, methodName, null, @params);
37-
lambda = Expression.Lambda(call, lambdaParams).Compile();
38-
}
39-
40-
ExpressionCache.Invoke.TryAdd(key, lambda);
41-
}
42-
4313
object?[] invokeParams;
4414

4515
if (args.Length == 0)
@@ -53,7 +23,36 @@ public static class MethodOf
5323
Array.Copy(args, 0, invokeParams, 1, args.Length);
5424
}
5525

56-
return (T?)lambda.DynamicInvoke(invokeParams);
26+
var key = new Tuple<Type, string>(@this.GetType(), methodName);
27+
var @delegate = ExpressionCache.Invoke.GetOrAdd(key, x =>
28+
{
29+
return new Lazy<Delegate>(() =>
30+
{
31+
var param = Expression.Parameter(x.Item1);
32+
33+
if (args.Length == 0)
34+
{
35+
var call = Expression.Call(param, x.Item2, null);
36+
return Expression.Lambda(call, param).Compile();
37+
}
38+
else
39+
{
40+
var @params = new Expression[args.Length];
41+
42+
for (var i = 0; i < args.Length; i++)
43+
@params[i] = Expression.Parameter(args[i].GetType());
44+
45+
var lambdaParams = new ParameterExpression[args.Length + 1];
46+
lambdaParams[0] = param;
47+
Array.Copy(@params, 0, lambdaParams, 1, @params.Length);
48+
49+
var call = Expression.Call(param, x.Item2, null, @params);
50+
return Expression.Lambda(call, lambdaParams).Compile();
51+
}
52+
});
53+
}).Value;
54+
55+
return (T?)@delegate.DynamicInvoke(invokeParams);
5756
}
5857
}
5958
}

src/PropertyOf.cs

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,36 +17,36 @@ public static class PropertyOf
1717

1818
public static T? Get<T>(object source, string propertyName)
1919
{
20-
var type = source.GetType();
21-
var key = new Tuple<Type, string>(type, propertyName);
22-
23-
if (!ExpressionCache.PropertyGetters.TryGetValue(key, out var @delegate))
20+
var key = new Tuple<Type, string>(source.GetType(), propertyName);
21+
var @delegate = ExpressionCache.PropertyGetters.GetOrAdd(key, static x =>
2422
{
25-
var param = Expression.Parameter(type);
26-
var prop = Expression.Property(param, propertyName);
23+
return new Lazy<Delegate>(() =>
24+
{
25+
var param = Expression.Parameter(x.Item1);
26+
var prop = Expression.Property(param, x.Item2);
2727

28-
@delegate = Expression.Lambda(prop, param).Compile();
29-
ExpressionCache.PropertyGetters.TryAdd(key, @delegate);
30-
}
28+
return Expression.Lambda(prop, param).Compile();
29+
});
30+
}).Value;
3131

3232
return (T?)@delegate.DynamicInvoke(source);
3333
}
3434

3535
public static void Set<T>(object source, string propertyName, T value)
3636
{
37-
var type = source.GetType();
38-
var key = new Tuple<Type, string>(type, propertyName);
39-
40-
if (!ExpressionCache.PropertySetters.TryGetValue(key, out var @delegate))
37+
var key = new Tuple<Type, string>(source.GetType(), propertyName);
38+
var @delegate = ExpressionCache.PropertySetters.GetOrAdd(key, static x =>
4139
{
42-
var param = Expression.Parameter(type);
43-
var prop = Expression.Property(param, propertyName);
44-
var valueParam = Expression.Parameter(typeof(T));
45-
var assign = Expression.Assign(prop, valueParam);
40+
return new Lazy<Delegate>(() =>
41+
{
42+
var param = Expression.Parameter(x.Item1);
43+
var prop = Expression.Property(param, x.Item2);
44+
var valueParam = Expression.Parameter(typeof(T));
45+
var assign = Expression.Assign(prop, valueParam);
4646

47-
@delegate = Expression.Lambda(assign, param, valueParam).Compile();
48-
ExpressionCache.PropertySetters.TryAdd(key, @delegate);
49-
}
47+
return Expression.Lambda(assign, param, valueParam).Compile();
48+
});
49+
}).Value;
5050

5151
@delegate.DynamicInvoke(source, value);
5252
}
@@ -65,16 +65,17 @@ private static class Cache<TDestination>
6565
public static Func<T, TDestination> Get(string propertyName)
6666
{
6767
var key = new Tuple<Type, string>(typeof(T), propertyName);
68-
if (ExpressionCache.PropertyGetters.TryGetValue(key, out var @delegate))
69-
return (Func<T, TDestination>)@delegate;
70-
71-
var param = Expression.Parameter(typeof(T));
72-
var prop = Expression.Property(param, propertyName);
7368

74-
var lambda = Expression.Lambda<Func<T, TDestination>>(prop, param).Compile();
75-
ExpressionCache.PropertyGetters.TryAdd(key, lambda);
69+
return (Func<T, TDestination>)ExpressionCache.PropertyGetters.GetOrAdd(key, static x =>
70+
{
71+
return new Lazy<Delegate>(() =>
72+
{
73+
var param = Expression.Parameter(x.Item1);
74+
var prop = Expression.Property(param, x.Item2);
7675

77-
return lambda;
76+
return Expression.Lambda<Func<T, TDestination>>(prop, param).Compile();
77+
});
78+
}).Value;
7879
}
7980
}
8081
}

tests/MyNihongo.Expressions.Tests/MethodOfTests/InvokeShould.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public void SaveExpressionInCache()
3434
const string method = nameof(InvokeCacheTest.GetResult);
3535
var key = new Tuple<Type, string>(typeof(InvokeCacheTest), method);
3636

37-
var dictionary = (ConcurrentDictionary<Tuple<Type, string>, Delegate>)typeof(ExpressionCache)
37+
var dictionary = (ConcurrentDictionary<Tuple<Type, string>, Lazy<Delegate>>)typeof(ExpressionCache)
3838
.GetField(nameof(ExpressionCache.Invoke), BindingFlags.Static | BindingFlags.NonPublic)
3939
!.GetValue(null);
4040

tests/MyNihongo.Expressions.Tests/PropertyOfTests/GetShould.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public void SaveExpressionsInCacheFromNonGeneric()
4545
const string prop = nameof(item.Prop);
4646
var key = new Tuple<Type, string>(item.GetType(), prop);
4747

48-
var dictionary = (ConcurrentDictionary<Tuple<Type, string>, Delegate>)typeof(ExpressionCache)
48+
var dictionary = (ConcurrentDictionary<Tuple<Type, string>, Lazy<Delegate>>)typeof(ExpressionCache)
4949
.GetField(nameof(ExpressionCache.PropertyGetters), BindingFlags.Static | BindingFlags.NonPublic)
5050
!.GetValue(null);
5151

@@ -71,7 +71,7 @@ public void SaveExpressionsInCacheFromGeneric()
7171
const string prop = nameof(item.Prop);
7272
var key = new Tuple<Type, string>(item.GetType(), prop);
7373

74-
var dictionary = (ConcurrentDictionary<Tuple<Type, string>, Delegate>)typeof(ExpressionCache)
74+
var dictionary = (ConcurrentDictionary<Tuple<Type, string>, Lazy<Delegate>>)typeof(ExpressionCache)
7575
.GetField(nameof(ExpressionCache.PropertyGetters), BindingFlags.Static | BindingFlags.NonPublic)
7676
!.GetValue(null);
7777

tests/MyNihongo.Expressions.Tests/PropertyOfTests/SetShould.cs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public void SaveExpressionsInCache()
3636
const string prop = nameof(item.Prop);
3737
var key = new Tuple<Type, string>(item.GetType(), prop);
3838

39-
var dictionary = (ConcurrentDictionary<Tuple<Type, string>, Delegate>)typeof(ExpressionCache)
39+
var dictionary = (ConcurrentDictionary<Tuple<Type, string>, Lazy<Delegate>>)typeof(ExpressionCache)
4040
.GetField(nameof(ExpressionCache.PropertySetters), BindingFlags.Static | BindingFlags.NonPublic)
4141
!.GetValue(null);
4242

@@ -62,17 +62,5 @@ public void ThrowExceptionIfPropertyNotFound()
6262
.Should()
6363
.ThrowExactly<ArgumentException>();
6464
}
65-
66-
[Fact]
67-
public void ThrowExceptionIfInvalidType()
68-
{
69-
var input = new TestRecord();
70-
71-
Action action = () => PropertyOf.Set(input, nameof(TestRecord.Text), 123);
72-
73-
action
74-
.Should()
75-
.ThrowExactly<ArgumentException>();
76-
}
7765
}
7866
}

0 commit comments

Comments
 (0)