-
Notifications
You must be signed in to change notification settings - Fork 772
Expand file tree
/
Copy pathDynamicObjectMemberAccessor.cs
More file actions
84 lines (71 loc) · 2.58 KB
/
DynamicObjectMemberAccessor.cs
File metadata and controls
84 lines (71 loc) · 2.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
using System;
using System.Dynamic;
using System.Runtime.CompilerServices;
using Microsoft.CSharp.RuntimeBinder;
namespace Python.Runtime;
class DynamicObjectMemberAccessor
{
const int MaxCacheEntries = 1000;
readonly ConcurrentLruCache<MemberKey, Func<object, object>> getters = new(MaxCacheEntries);
readonly ConcurrentLruCache<MemberKey, Action<object, object?>> setters = new(MaxCacheEntries);
static readonly CSharpArgumentInfo[] getArgumentInfo =
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
};
static readonly CSharpArgumentInfo[] setArgumentInfo =
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
};
public bool TryGetMember(IDynamicMetaObjectProvider obj, string memberName, out object? value)
{
if (obj is null)
throw new ArgumentNullException(nameof(obj));
if (memberName is null)
throw new ArgumentNullException(nameof(memberName));
var getter = getters.GetOrAdd(new MemberKey(obj.GetType(), memberName), static key =>
{
var binder = Binder.GetMember(CSharpBinderFlags.None, key.MemberName, key.Type, getArgumentInfo);
var callSite = CallSite<Func<CallSite, object, object>>.Create(binder);
return obj => callSite.Target(callSite, obj);
});
try
{
value = getter(obj);
return true;
}
catch (RuntimeBinderException)
{
value = null;
return false;
}
}
public bool TrySetMember(IDynamicMetaObjectProvider obj, string memberName, object? value)
{
if (obj is null)
throw new ArgumentNullException(nameof(obj));
if (memberName is null)
throw new ArgumentNullException(nameof(memberName));
var setter = setters.GetOrAdd(new MemberKey(obj.GetType(), memberName), static key =>
{
var binder = Binder.SetMember(CSharpBinderFlags.None, key.MemberName, key.Type, setArgumentInfo);
var callSite = CallSite<Action<CallSite, object, object?>>.Create(binder);
return (obj, value) => callSite.Target(callSite, obj, value);
});
try
{
setter(obj, value);
return true;
}
catch (RuntimeBinderException)
{
return false;
}
}
readonly record struct MemberKey(Type Type, string MemberName);
public void Clear()
{
getters.Clear();
setters.Clear();
}
}