Похожие презентации:
Туда и обратно. Тёмная сторона сериализации
1.
Туда и обратно.Тёмная сторона сериализации
Константин Рудниченко
CTO, Cadwise
2.
23.
О себе● Зарабатываю на жизнь программированием с
2005 года
● Программировал еще на .net framework 1.0
● Помню С# без Generics
○ Складывал объекты ArrayList
● Использовал BinaryFormatter ¯\_(ツ)_/¯
3
4.
“ЗОЧЕМ?Есть же <выберите свой
сериализатор>?”
4
5.
Варианты● Protocol Buffers
● Json
○ Newtonsoft.Json
○ System.Text.Json
○ etc
● BinaryFormatter
● XmlFormatter
● другие
5
6.
Причины6
7.
Причины● Экзотические форматы
7
8.
Причины● Экзотические форматы
● Лучшая производительность
8
9.
Причины● Экзотические форматы
● Лучшая производительность
● Другие сценарии использования Reflection:
○ Data-binding
○ Object-object mapping
○ Логирование: Destructing
9
10.
О докладе● Доступ к данным: Reflection и альтернативы
● Производительность различных способов доступа
● Участие доступа к данным в процессе
сериализации
10
11.
Прямой доступ vs. Reflection11
12.
1213.
System.Reflection● System.Object
○ GetType()
13
14.
System.Reflection● System.Object
○ GetType()
● System.Type
○ GetProperties(...)
14
15.
System.Reflection● System.Object
○ GetType()
● System.Type
○ GetProperties(...)
● System.Reflection.PropertyInfo
○ Name
○ PropertyType
○ GetValue/SetValue
15
16.
“Все знают, чтоReflection это Медленно!!!”
16
17.
Type System Overview17
18.
Насколько Reflection это медленно?18
19.
Цифры19
20.
2021.
2122.
2223.
2324.
2425.
2526.
2627.
2728.
Ba Dum Tss!28
29.
Ba Dum Tss!29
30.
“Давай закэшируем PropertyInfo!”30
31.
3132.
Cached PropertyInfo32
33.
Cached PropertyInfo33
34.
3435.
“Неужели нет другого способа?!”35
36.
3637.
FastMember37
38.
FastMember38
39.
FastMember39
40.
4041.
4142.
“Безнадёга?”42
43.
Delegate.CreateDelegate43
44.
DelegateОткрытые / Закрытые
44
45.
Delegate: открытые/закрытые45
46.
Delegate: открытые/закрытые46
47.
Delegate: открытые/закрытые47
48.
Delegate.CreateDelegate48
49.
Delegate.CreateDelegate49
50.
PropertyInfo ➝ MethodInfo50
51.
PropertyInfo ➝ MethodInfoGetGetMethod() / GetSetMethod()
51
52.
Delegate.CreateDelegate52
53.
Delegate.CreateDelegate53
54.
Delegate.CreateDelegate54
55.
Delegate.CreateDelegate55
56.
Delegate.CreateDelegate56
57.
Delegate.CreateDelegate57
58.
5859.
5960.
“А может генерировать код в runtime?”60
61.
Генерация кода в runtime61
62.
Генерация кода в runtime● IL Emit
62
63.
Генерация кода в runtime● IL Emit
● Compiled Expression Trees
63
64.
IL Emit64
65.
Intermediate Language65
66.
6667.
public string GetViaProperty() => _test.StringProperty;67
68.
public string GetViaProperty() => _test.StringProperty;.method private hidebysig
static string GetViaProperty( class Test target)
cil managed
{
.maxstack 1
ldarg.0
callvirt instance string Test::get_StringProperty()
ret
}
68
69.
public static Func<TTarget, TParam> GenerateGetter<TTarget, TParam>(PropertyInfo property){
var method = new DynamicMethod(property.Name + "GetterTyped", typeof(TParam),
new[] { typeof(TTarget) },
Module, true);
var gen = method.GetILGenerator();
var getMethod = property.GetGetMethod(true);
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Call, getMethod);
gen.Emit(OpCodes.Ret);
return (Func<TTarget, TParam>)method.CreateDelegate(typeof(Func<TTarget, TParam>));
}
69
70.
public static Func<TTarget, TParam> GenerateGetter<TTarget, TParam>(PropertyInfo property){
var method = new DynamicMethod(property.Name + "GetterTyped", typeof(TParam),
new[] { typeof(TTarget) },
Module, true);
var gen = method.GetILGenerator();
var getMethod = property.GetGetMethod(true);
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Call, getMethod);
gen.Emit(OpCodes.Ret);
return (Func<TTarget, TParam>)method.CreateDelegate(typeof(Func<TTarget, TParam>));
}
70
71.
public static Func<TTarget, TParam> GenerateGetter<TTarget, TParam>(PropertyInfo property){
var method = new DynamicMethod(property.Name + "GetterTyped", typeof(TParam),
new[] { typeof(TTarget) },
Module, true);
var gen = method.GetILGenerator();
var getMethod = property.GetGetMethod(true);
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Call, getMethod);
gen.Emit(OpCodes.Ret);
return (Func<TTarget, TParam>)method.CreateDelegate(typeof(Func<TTarget, TParam>));
}
71
72.
public static Func<TTarget, TParam> GenerateGetter<TTarget, TParam>(PropertyInfo property){
var method = new DynamicMethod(property.Name + "GetterTyped", typeof(TParam),
new[] { typeof(TTarget) },
Module, true);
var gen = method.GetILGenerator();
var getMethod = property.GetGetMethod(true);
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Call, getMethod);
gen.Emit(OpCodes.Ret);
return (Func<TTarget, TParam>)method.CreateDelegate(typeof(Func<TTarget, TParam>));
}
72
73.
public static Func<TTarget, TParam> GenerateGetter<TTarget, TParam>(PropertyInfo property){
var method = new DynamicMethod(property.Name + "GetterTyped", typeof(TParam),
new[] { typeof(TTarget) },
Module, true);
var gen = method.GetILGenerator();
var getMethod = property.GetGetMethod(true);
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Call, getMethod);
gen.Emit(OpCodes.Ret);
return (Func<TTarget, TParam>)method.CreateDelegate(typeof(Func<TTarget, TParam>));
}
73
74.
public static Func<TTarget, TParam> GenerateGetter<TTarget, TParam>(PropertyInfo property){
var method = new DynamicMethod(property.Name + "GetterTyped", typeof(TParam),
new[] { typeof(TTarget) },
Module, true);
var gen = method.GetILGenerator();
var getMethod = property.GetGetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Call, getMethod);
gen.Emit(OpCodes.Ret);
return (Func<TTarget, TParam>)method.CreateDelegate(typeof(Func<TTarget, TParam>));
}
74
75.
public static Func<TTarget, TParam> GenerateGetter<TTarget, TParam>(PropertyInfo property){
var method = new DynamicMethod(property.Name + "GetterTyped", typeof(TParam),
new[] { typeof(TTarget) },
Module, true);
var gen = method.GetILGenerator();
var getMethod = property.GetGetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Call, getMethod);
gen.Emit(OpCodes.Ret);
return (Func<TTarget, TParam>)method.CreateDelegate(typeof(Func<TTarget, TParam>));
}
75
76.
public static Func<TTarget, TParam> GenerateGetter<TTarget, TParam>(PropertyInfo property){
var method = new DynamicMethod(property.Name + "GetterTyped", typeof(TParam),
new[] { typeof(TTarget) },
Module, true);
var gen = method.GetILGenerator();
var getMethod = property.GetGetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Call, getMethod);
gen.Emit(OpCodes.Ret);
return (Func<TTarget, TParam>)method.CreateDelegate(typeof(Func<TTarget, TParam>));
}
76
77.
public static Func<TTarget, TParam> GenerateGetter<TTarget, TParam>(PropertyInfo property){
var method = new DynamicMethod(property.Name + "GetterTyped", typeof(TParam),
new[] { typeof(TTarget) },
Module, true);
var gen = method.GetILGenerator();
var getMethod = property.GetGetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Call, getMethod);
gen.Emit(OpCodes.Ret);
return (Func<TTarget, TParam>)method.CreateDelegate(typeof(Func<TTarget, TParam>));
}
77
78.
public static Func<TTarget, TParam> GenerateGetter<TTarget, TParam>(PropertyInfo property){
var method = new DynamicMethod(property.Name + "GetterTyped", typeof(TParam),
new[] { typeof(TTarget) },
Module, true);
var gen = method.GetILGenerator();
var getMethod = property.GetGetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Call, getMethod);
gen.Emit(OpCodes.Ret);
return (Func<TTarget, TParam>)method.CreateDelegate(typeof(Func<TTarget, TParam>));
}
78
79.
public static Func<TTarget, TParam> GenerateGetter<TTarget, TParam>(PropertyInfo property){
var method = new DynamicMethod(property.Name + "GetterTyped", typeof(TParam),
new[] { typeof(TTarget) },
Module, true);
var gen = method.GetILGenerator();
var getMethod = property.GetGetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Call, getMethod);
gen.Emit(OpCodes.Ret);
return (Func<TTarget, TParam>)method.CreateDelegate(typeof(Func<TTarget, TParam>));
}
79
80.
static Action<TTarget, TParam> GenerateSetter<TTarget, TParam>(PropertyInfo propertyInfo){
var method = new DynamicMethod(propertyInfo.Name + "SetterTyped", null,
new[] { typeof(TTarget), typeof(TParam) }, Module, true);
var gen = method.GetILGenerator();
var setMethod = propertyInfo.GetSetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Call, setMethod);
gen.Emit(OpCodes.Ret);
return (Action<TTarget, TParam>)method.CreateDelegate(typeof(Action<TTarget, TParam>));
}
80
81.
static Action<TTarget, TParam> GenerateSetter<TTarget, TParam>(PropertyInfo propertyInfo){
var method = new DynamicMethod(propertyInfo.Name + "SetterTyped", null,
new[] { typeof(TTarget), typeof(TParam) }, Module, true);
var gen = method.GetILGenerator();
var setMethod = propertyInfo.GetSetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Call, setMethod);
gen.Emit(OpCodes.Ret);
return (Action<TTarget, TParam>)method.CreateDelegate(typeof(Action<TTarget, TParam>));
}
81
82.
static Action<TTarget, TParam> GenerateSetter<TTarget, TParam>(PropertyInfo propertyInfo){
var method = new DynamicMethod(propertyInfo.Name + "SetterTyped", null,
new[] { typeof(TTarget), typeof(TParam) }, Module, true);
var gen = method.GetILGenerator();
var setMethod = propertyInfo.GetSetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Call, setMethod);
gen.Emit(OpCodes.Ret);
return (Action<TTarget, TParam>)method.CreateDelegate(typeof(Action<TTarget, TParam>));
}
82
83.
static Action<TTarget, TParam> GenerateSetter<TTarget, TParam>(PropertyInfo propertyInfo){
var method = new DynamicMethod(propertyInfo.Name + "SetterTyped", null,
new[] { typeof(TTarget), typeof(TParam) }, Module, true);
var gen = method.GetILGenerator();
var setMethod = propertyInfo.GetSetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Call, setMethod);
gen.Emit(OpCodes.Ret);
return (Action<TTarget, TParam>)method.CreateDelegate(typeof(Action<TTarget, TParam>));
}
83
84.
static Action<TTarget, TParam> GenerateSetter<TTarget, TParam>(PropertyInfo propertyInfo){
var method = new DynamicMethod(propertyInfo.Name + "SetterTyped", null,
new[] { typeof(TTarget), typeof(TParam) }, Module, true);
var gen = method.GetILGenerator();
var setMethod = propertyInfo.GetSetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Call, setMethod);
gen.Emit(OpCodes.Ret);
return (Action<TTarget, TParam>)method.CreateDelegate(typeof(Action<TTarget, TParam>));
}
84
85.
static Action<TTarget, TParam> GenerateSetter<TTarget, TParam>(PropertyInfo propertyInfo){
var method = new DynamicMethod(propertyInfo.Name + "SetterTyped", null,
new[] { typeof(TTarget), typeof(TParam) }, Module, true);
var gen = method.GetILGenerator();
var setMethod = propertyInfo.GetSetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Call, setMethod);
gen.Emit(OpCodes.Ret);
return (Action<TTarget, TParam>)method.CreateDelegate(typeof(Action<TTarget, TParam>));
}
85
86.
static Action<TTarget, TParam> GenerateSetter<TTarget, TParam>(PropertyInfo propertyInfo){
var method = new DynamicMethod(propertyInfo.Name + "SetterTyped", null,
new[] { typeof(TTarget), typeof(TParam) }, Module, true);
var gen = method.GetILGenerator();
var setMethod = propertyInfo.GetSetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Call, setMethod);
gen.Emit(OpCodes.Ret);
return (Action<TTarget, TParam>)method.CreateDelegate(typeof(Action<TTarget, TParam>));
}
86
87.
static Action<TTarget, TParam> GenerateSetter<TTarget, TParam>(PropertyInfo propertyInfo){
var method = new DynamicMethod(propertyInfo.Name + "SetterTyped", null,
new[] { typeof(TTarget), typeof(TParam) }, Module, true);
var gen = method.GetILGenerator();
var setMethod = propertyInfo.GetSetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Call, setMethod);
gen.Emit(OpCodes.Ret);
return (Action<TTarget, TParam>)method.CreateDelegate(typeof(Action<TTarget, TParam>));
}
87
88.
static Action<TTarget, TParam> GenerateSetter<TTarget, TParam>(PropertyInfo propertyInfo){
var method = new DynamicMethod(propertyInfo.Name + "SetterTyped", null,
new[] { typeof(TTarget), typeof(TParam) }, Module, true);
var gen = method.GetILGenerator();
var setMethod = propertyInfo.GetSetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Call, setMethod);
gen.Emit(OpCodes.Ret);
return (Action<TTarget, TParam>)method.CreateDelegate(typeof(Action<TTarget, TParam>));
}
88
89.
static Action<TTarget, TParam> GenerateSetter<TTarget, TParam>(PropertyInfo propertyInfo){
var method = new DynamicMethod(propertyInfo.Name + "SetterTyped", null,
new[] { typeof(TTarget), typeof(TParam) }, Module, true);
var gen = method.GetILGenerator();
var setMethod = propertyInfo.GetSetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Call, setMethod);
gen.Emit(OpCodes.Ret);
return (Action<TTarget, TParam>)method.CreateDelegate(typeof(Action<TTarget, TParam>));
}
89
90.
static Action<TTarget, TParam> GenerateSetter<TTarget, TParam>(PropertyInfo propertyInfo){
var method = new DynamicMethod(propertyInfo.Name + "SetterTyped", null,
new[] { typeof(TTarget), typeof(TParam) }, Module, true);
var gen = method.GetILGenerator();
var setMethod = propertyInfo.GetSetMethod();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Call, setMethod);
gen.Emit(OpCodes.Ret);
return (Action<TTarget, TParam>)method.CreateDelegate(typeof(Action<TTarget, TParam>));
}
90
91.
Intermediate Language91
92.
Intermediate Language92
93.
Intermediate Language93
94.
Compiled Expression Trees94
95.
Expression Trees95
96.
Expression Trees● Lambda Expressions
96
97.
Expression Trees● Lambda Expressions
● API
97
98.
Expression Trees: Lambda98
99.
Expression Trees: Lambda99
100.
Expression Trees: API100
101.
101102.
102103.
103104.
104105.
105106.
106107.
107108.
108109.
109110.
110111.
111112.
112113.
113114.
114115.
Expression Trees: API115
116.
Expression Trees: API116
117.
Expression Trees: API117
118.
Expression Trees: API118
119.
119120.
120121.
Ни рефлексией единой● Reflection - медленно
121
122.
Ни рефлексией единой● Reflection - медленно
● Альтернативы “из коробки” - быстрее
122
123.
Ни рефлексией единой● Reflection - медленно
● Альтернативы “из коробки” - быстрее
○ Требуют усилий
123
124.
“Погоди! Но ...”124
125.
“Делегаты специфицированыконкретными типами целевого
объекта и его свойства!”
125
126.
126127.
127128.
128129.
PropertyInfo.GetValue129
130.
Delegate.CreateDelegate130
131.
Delegate.CreateDelegate131
132.
System.ArgumentExceptionCannot bind to the target method because its signature is not compatible
with that of the delegate type.
at System.Delegate.CreateDelegate(Type type, MethodInfo method,
Boolean throwOnBindFailure)
at System.Delegate.CreateDelegate(Type type, MethodInfo method)
at Program.Main() in Program.cs
132
133.
“Эх! А счастье было так близко...”133
134.
Delegate (object, object)134
135.
Delegate (object, object)135
136.
Delegate (object, object)136
137.
Delegate (object, object)137
138.
Delegate (object, object)138
139.
Delegate (object, object)139
140.
Delegate (object, object)140
141.
Delegate (object, object)141
142.
Delegate (object, object)142
143.
143144.
Delegate (object, object)144
145.
Delegate (object, object)145
146.
ILGen (object, object)146
147.
ILGen (object, object)147
148.
ILGen (object, object)148
149.
ILGen (object, object)149
150.
ILGen (object, object)150
151.
ILGen (object, object)151
152.
Expression Trees (object, object)152
153.
Expression Trees (object, object)153
154.
Expression Trees (object, object)154
155.
Expression Trees (object, object)155
156.
Expression Trees (object, object)156
157.
157158.
158159.
Ни рефлексией единой● Reflection - медленно
159
160.
Ни рефлексией единой● Reflection - медленно
○ Нетипизированный доступ
160
161.
Ни рефлексией единой● Reflection - медленно
○ Нетипизированный доступ
● Альтернативы “из коробки” - быстрее
161
162.
Ни рефлексией единой● Reflection - медленно
○ Нетипизированный доступ
● Альтернативы “из коробки” - быстрее
○ Типизированный доступ
162
163.
Ни рефлексией единой● Reflection - медленно
○ Нетипизированный доступ
● Альтернативы “из коробки” - быстрее
○ Типизированный доступ
○ Reflection-like
163
164.
Ни рефлексией единой● Reflection - медленно
○ Нетипизированный доступ
● Альтернативы “из коробки” - быстрее
○ Типизированный доступ
○ Reflection-like
○ Требуют усилий
164
165.
“А что если ValueType? Boxing же!”165
166.
ValueType166
167.
ValueType167
168.
ValueType168
169.
169170.
170171.
171172.
Ни рефлексией единой: Value Type172
173.
Ни рефлексией единой: Value Type● Reflection: boxing
173
174.
Ни рефлексией единой: Value Type● Reflection: boxing
● Альтернатива
○ Reflection-like: boxing
174
175.
Ни рефлексией единой: Value Type● Reflection: boxing
● Альтернатива
○ Reflection-like: boxing
○ Типизированный доступ: no boxing
175
176.
Приватные данные176
177.
177178.
178179.
179180.
180181.
Доступ к данным181
182.
Доступ к данным● Reflection
182
183.
Доступ к данным● Reflection:
○ Медленно
183
184.
Доступ к данным● Reflection:
○ Медленно
○ Boxing
184
185.
Доступ к данным● Reflection:
○ Медленно
○ Boxing
● Альтернативы
185
186.
Доступ к данным● Reflection:
○ Медленно
○ Boxing
● Альтернативы:
○ Сильно быстрее Reflection
186
187.
Доступ к данным● Reflection:
○ Медленно
○ Boxing
● Альтернативы:
○ Сильно быстрее Reflection
○ Boxing
187
188.
Доступ к данным● Reflection:
○ Медленно
○ Boxing
● Альтернативы:
○ Сильно быстрее Reflection
○ Boxing / No Boxing
188
189.
Доступ к данным● Reflection:
○ Медленно
○ Boxing
● Альтернативы:
○ Сильно быстрее Reflection
○ Boxing / No Boxing
● Можно и приватные данные (поля, свойства)
189
190.
Доступ к данным● Reflection:
○ Медленно
○ Boxing
● Альтернативы:
○ Сильно быстрее Reflection
○ Boxing / No Boxing
● Можно и приватные данные (поля, свойства)
● Прямое чтение/запись не обойти
190
191.
191192.
Сценарий использования192
193.
Сериализация193
194.
Сериализация194
195.
Сериализация195
196.
Сериализация196
197.
Сериализация197
198.
Сериализация198
199.
Но!199
200.
Сгенерированный сериализатор200
201.
Сгенерированный сериализатор201
202.
Сериализация202
203.
203204.
204205.
205206.
206207.
ValueType. Reflection-like207
208.
ValueType. Generated208
209.
209210.
SerializerSize in bytes
Google.Protobuf
17
Custom(Property)
18
Custom(Expression Trees)
18
BinaryFormatter
236
210
211.
211212.
212213.
213214.
214215.
Ни рефлексией единойBinaryFormatter
Newtonsoft.Json
System.Text.Json
Google.Protobuf
AutoMapper
215
216.
Ни рефлексией единойBinaryFormatter: Reflection
Newtonsoft.Json
System.Text.Json
Google.Protobuf
AutoMapper
216
217.
Ни рефлексией единойBinaryFormatter: Reflection ¯\_(ツ)_/¯
Newtonsoft.Json
System.Text.Json
Google.Protobuf
AutoMapper
217
218.
Ни рефлексией единойBinaryFormatter: Reflection
Newtonsoft.Json: IL Emit
System.Text.Json
Google.Protobuf
AutoMapper
218
219.
Ни рефлексией единойBinaryFormatter: Reflection
Newtonsoft.Json: IL Emit
System.Text.Json: IL Emit
Google.Protobuf
AutoMapper
219
220.
Ни рефлексией единойBinaryFormatter: Reflection
Newtonsoft.Json: IL Emit
System.Text.Json: IL Emit
Google.Protobuf: Property
AutoMapper
220
221.
Ни рефлексией единойBinaryFormatter: Reflection
Newtonsoft.Json: IL Emit
System.Text.Json: IL Emit
Google.Protobuf: Property (Генерация C# - кода)
AutoMapper
221
222.
Ни рефлексией единойBinaryFormatter: Reflection
Newtonsoft.Json: IL Emit
System.Text.Json: IL Emit
Google.Protobuf: Property (Генерация C# - кода)
AutoMapper: Expression Trees
222
223.
Осталось за скобкамиБезопасность
Инстанцирование объектов (new, CreateInstance)
Парсинг данных
Схема и версионирование данных
Генерация C# - кода
223
224.
Ни рефлексией единой● Reflection - медленно
● Есть достойные альтернативы:
○ CreateDelegate / IL Emit / Expression Trees
● Доступ к данным еще не всё
● Runtime генерация кода для “сложных” случаев
224
225.
225226.
Константин Рудниченкотехнический директор Cadwise
[email protected]
https://t.me/krudnichenko
https://twitter.com/KRudnichenko
http://cadwise.ru
226