Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit 68c7dc2

Browse files
committed
Add TypeFields for maintaining cached setters for populating a Types fields
1 parent bc29be2 commit 68c7dc2

File tree

3 files changed

+179
-0
lines changed

3 files changed

+179
-0
lines changed

src/ServiceStack.Text/TypeFields.cs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
using System.Threading;
5+
using ServiceStack.Reflection;
6+
using ServiceStack.Text;
7+
using ServiceStack.Text.Common;
8+
9+
namespace ServiceStack
10+
{
11+
public class TypeFields<T> : TypeFields
12+
{
13+
public static readonly TypeFields<T> Instance = new TypeFields<T>();
14+
15+
public readonly Dictionary<string, SetPropertyDelegateRefGeneric<T>> GenericPublicSetters =
16+
new Dictionary<string, SetPropertyDelegateRefGeneric<T>>(PclExport.Instance.InvariantComparerIgnoreCase);
17+
18+
static TypeFields()
19+
{
20+
Instance.Type = typeof(T);
21+
Instance.PublicFieldInfos = typeof(T).GetPublicFields();
22+
foreach (var fi in Instance.PublicFieldInfos)
23+
{
24+
try
25+
{
26+
Instance.PublicGetters[fi.Name] = PclExport.Instance.GetFieldGetterFn(fi);
27+
Instance.PublicSetters[fi.Name] = fi.GetValueSetter(typeof(T));
28+
Instance.GenericPublicSetters[fi.Name] = fi.GetValueSetterGenericRef<T>();
29+
Instance.PublicFields[fi.Name] = fi;
30+
}
31+
catch (Exception ex)
32+
{
33+
Tracer.Instance.WriteError(ex);
34+
}
35+
}
36+
}
37+
38+
public override SetPropertyDelegateRef GetPublicSetterRef(string name)
39+
{
40+
if (name == null)
41+
return null;
42+
43+
return GenericPublicSetters.TryGetValue(name, out SetPropertyDelegateRefGeneric<T> fn)
44+
? delegate (ref object instance, object arg)
45+
{
46+
var valueInstance = (T)instance;
47+
fn(ref valueInstance, arg);
48+
instance = valueInstance;
49+
}
50+
: (SetPropertyDelegateRef)null;
51+
}
52+
}
53+
54+
public abstract class TypeFields
55+
{
56+
static Dictionary<Type, TypeFields> CacheMap = new Dictionary<Type, TypeFields>();
57+
58+
public static TypeFields Get(Type type)
59+
{
60+
if (CacheMap.TryGetValue(type, out TypeFields value))
61+
return value;
62+
63+
var genericType = typeof(TypeFields<>).MakeGenericType(type);
64+
var instanceFi = genericType.GetPublicStaticField("Instance");
65+
var instance = (TypeFields)instanceFi.GetValue(null);
66+
67+
Dictionary<Type, TypeFields> snapshot, newCache;
68+
do
69+
{
70+
snapshot = CacheMap;
71+
newCache = new Dictionary<Type, TypeFields>(CacheMap) {
72+
[type] = instance
73+
};
74+
} while (!ReferenceEquals(
75+
Interlocked.CompareExchange(ref CacheMap, newCache, snapshot), snapshot));
76+
77+
return instance;
78+
}
79+
80+
public Type Type;
81+
82+
public readonly Dictionary<string, PropertyGetterDelegate> PublicGetters =
83+
new Dictionary<string, PropertyGetterDelegate>(PclExport.Instance.InvariantComparerIgnoreCase);
84+
85+
public readonly Dictionary<string, Action<object, object>> PublicSetters =
86+
new Dictionary<string, Action<object, object>>(PclExport.Instance.InvariantComparerIgnoreCase);
87+
88+
public readonly Dictionary<string, FieldInfo> PublicFields =
89+
new Dictionary<string, FieldInfo>(PclExport.Instance.InvariantComparerIgnoreCase);
90+
91+
public FieldInfo[] PublicFieldInfos { get; protected set; }
92+
93+
public virtual FieldInfo GetPublicField(string name)
94+
{
95+
foreach (var fi in PublicFieldInfos)
96+
{
97+
if (fi.Name == name)
98+
return fi;
99+
}
100+
return null;
101+
}
102+
103+
public virtual PropertyGetterDelegate GetPublicGetter(FieldInfo fi)
104+
{
105+
if (fi == null)
106+
return null;
107+
108+
return PublicGetters.TryGetValue(fi.Name, out PropertyGetterDelegate fn)
109+
? fn
110+
: PclExport.Instance.GetFieldGetterFn(fi);
111+
}
112+
113+
public virtual PropertyGetterDelegate GetPublicGetter(string name)
114+
{
115+
if (name == null)
116+
return null;
117+
118+
return PublicGetters.TryGetValue(name, out PropertyGetterDelegate fn)
119+
? fn
120+
: null;
121+
}
122+
123+
public virtual Action<object, object> GetPublicSetter(FieldInfo fi)
124+
{
125+
if (fi == null)
126+
return null;
127+
128+
return PublicSetters.TryGetValue(fi.Name, out Action<object, object> fn)
129+
? fn
130+
: fi.GetValueSetter(Type);
131+
}
132+
133+
public virtual Action<object, object> GetPublicSetter(string name)
134+
{
135+
if (name == null)
136+
return null;
137+
138+
return PublicSetters.TryGetValue(name, out Action<object, object> fn)
139+
? fn
140+
: null;
141+
}
142+
143+
public virtual SetPropertyDelegateRef GetPublicSetterRef(string name)
144+
{
145+
throw new NotImplementedException();
146+
}
147+
}
148+
}

tests/ServiceStack.Text.Tests/ServiceStack.Text.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
<ItemGroup>
2828
<PackageReference Include="NUnit" Version="3.6.1" />
29+
<PackageReference Include="System.ValueTuple" Version="4.3.1" />
2930
</ItemGroup>
3031

3132
<PropertyGroup Condition=" '$(TargetFramework)' == 'net45' ">
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using NUnit.Framework;
2+
3+
namespace ServiceStack.Text.Tests
4+
{
5+
public class TypeFieldReflectorTests
6+
{
7+
(string s, int i, long l, double d) CreateValueTuple() =>
8+
("foo", 1, 2, 3.3);
9+
10+
[Test]
11+
public void Can_cache_ValueTuple_field_accessors()
12+
{
13+
var typeAccessor = TypeFields.Get(typeof((string s, int i, long l, double d)));
14+
15+
var oTuple = (object)CreateValueTuple();
16+
17+
typeAccessor.GetPublicSetterRef("Item1")(ref oTuple, "bar");
18+
typeAccessor.GetPublicSetterRef("Item2")(ref oTuple, 10);
19+
typeAccessor.GetPublicSetterRef("Item3")(ref oTuple, 20L);
20+
typeAccessor.GetPublicSetterRef("Item4")(ref oTuple, 4.4d);
21+
22+
var tuple = ((string s, int i, long l, double d))oTuple;
23+
24+
Assert.That(tuple.s, Is.EqualTo("bar"));
25+
Assert.That(tuple.i, Is.EqualTo(10));
26+
Assert.That(tuple.l, Is.EqualTo(20));
27+
Assert.That(tuple.d, Is.EqualTo(4.4));
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)