com.wallstop-studios.unity-helpers 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/AGENTS.md +1 -0
  2. package/Docs/ILIST_SORTING_PERFORMANCE.md +92 -0
  3. package/{package-lock.json.meta → Docs/ILIST_SORTING_PERFORMANCE.md.meta} +1 -1
  4. package/Docs/INDEX.md +11 -1
  5. package/Docs/Images/random_generators.svg +7 -7
  6. package/Docs/RANDOM_PERFORMANCE.md +17 -14
  7. package/Docs/REFLECTION_HELPERS.md +84 -1
  8. package/Docs/REFLECTION_PERFORMANCE.md +169 -0
  9. package/Docs/REFLECTION_PERFORMANCE.md.meta +7 -0
  10. package/Docs/RELATIONAL_COMPONENTS.md +6 -0
  11. package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md +63 -0
  12. package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md.meta +7 -0
  13. package/Docs/SPATIAL_TREE_2D_PERFORMANCE.md +64 -64
  14. package/Docs/SPATIAL_TREE_3D_PERFORMANCE.md +64 -64
  15. package/Editor/Core/Helper/AnimationEventHelpers.cs +1 -1
  16. package/Editor/Sprites/AnimationCopier.cs +1 -1
  17. package/Editor/Sprites/AnimationViewerWindow.cs +4 -4
  18. package/Editor/Sprites/SpriteSettingsApplierAPI.cs +2 -1
  19. package/Editor/Sprites/TextureResizerWizard.cs +4 -3
  20. package/Editor/Utils/ScriptableObjectSingletonCreator.cs +3 -3
  21. package/README.md +33 -18
  22. package/Runtime/Core/Attributes/BaseRelationalComponentAttribute.cs +147 -20
  23. package/Runtime/Core/Attributes/ChildComponentAttribute.cs +630 -117
  24. package/Runtime/Core/Attributes/NotNullAttribute.cs +5 -2
  25. package/Runtime/Core/Attributes/ParentComponentAttribute.cs +477 -103
  26. package/Runtime/Core/Attributes/RelationalComponentAssigner.cs +26 -3
  27. package/Runtime/Core/Attributes/RelationalComponentExtensions.cs +19 -3
  28. package/Runtime/Core/Attributes/SiblingComponentAttribute.cs +265 -92
  29. package/Runtime/Core/CodeGen.meta +8 -0
  30. package/Runtime/Core/DataStructure/ImmutableBitSet.cs +5 -20
  31. package/Runtime/Core/Extension/IListExtensions.cs +720 -12
  32. package/Runtime/Core/Helper/Logging/UnityLogTagFormatter.cs +11 -7
  33. package/Runtime/Core/Helper/Objects.cs +1 -1
  34. package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs +5142 -0
  35. package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs.meta +11 -0
  36. package/Runtime/Core/Helper/ReflectionHelpers.cs +1812 -1518
  37. package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +2 -3
  38. package/Runtime/Core/Math/Line2D.cs +2 -4
  39. package/Runtime/Core/Math/Line3D.cs +2 -4
  40. package/Runtime/Core/Random/AbstractRandom.cs +52 -5
  41. package/Runtime/Core/Random/DotNetRandom.cs +3 -3
  42. package/Runtime/Core/Random/FlurryBurstRandom.cs +279 -0
  43. package/Runtime/Core/Random/FlurryBurstRandom.cs.meta +3 -0
  44. package/Runtime/Core/Random/IllusionFlow.cs +3 -3
  45. package/Runtime/Core/Random/LinearCongruentialGenerator.cs +3 -3
  46. package/Runtime/Core/Random/PcgRandom.cs +6 -6
  47. package/Runtime/Core/Random/PhotonSpinRandom.cs +387 -0
  48. package/Runtime/Core/Random/PhotonSpinRandom.cs.meta +3 -0
  49. package/Runtime/Core/Random/RomuDuo.cs +3 -3
  50. package/Runtime/Core/Random/SplitMix64.cs +3 -3
  51. package/Runtime/Core/Random/SquirrelRandom.cs +6 -4
  52. package/Runtime/Core/Random/StormDropRandom.cs +271 -0
  53. package/Runtime/Core/Random/StormDropRandom.cs.meta +3 -0
  54. package/Runtime/Core/Random/UnityRandom.cs +3 -3
  55. package/Runtime/Core/Random/WyRandom.cs +6 -4
  56. package/Runtime/Core/Random/XorShiftRandom.cs +3 -3
  57. package/Runtime/Core/Random/XoroShiroRandom.cs +3 -3
  58. package/Runtime/Tags/AttributeMetadataCache.cs +316 -9
  59. package/Runtime/Tags/CosmeticEffectData.cs +1 -1
  60. package/Runtime/Visuals/UIToolkit/MultiFileSelectorElement.cs +3 -3
  61. package/Tests/Editor/Helper/HelpersTests.cs +2 -2
  62. package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs +87 -0
  63. package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs.meta +11 -0
  64. package/Tests/Editor/Helper/SpriteHelpersTests.cs +1 -1
  65. package/Tests/Editor/PrefabCheckerReportTests.cs +3 -3
  66. package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +18 -12
  67. package/Tests/Editor/Sprites/AnimationCopierWindowTests.cs +8 -7
  68. package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +2 -1
  69. package/Tests/Editor/Sprites/ScriptableSpriteAtlasEditorTests.cs +6 -5
  70. package/Tests/Editor/Sprites/SpriteCropperAdditionalTests.cs +2 -1
  71. package/Tests/Editor/Sprites/SpriteCropperTests.cs +7 -6
  72. package/Tests/Editor/Sprites/SpritePivotAdjusterAdditionalTests.cs +2 -1
  73. package/Tests/Editor/Sprites/SpritePivotAdjusterTests.cs +4 -3
  74. package/Tests/Editor/Sprites/TextureResizerWizardTests.cs +10 -9
  75. package/Tests/Editor/Sprites/TextureSettingsApplierAPITests.cs +2 -1
  76. package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs +192 -0
  77. package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs.meta +11 -0
  78. package/Tests/Editor/Tags.meta +8 -0
  79. package/Tests/Runtime/Extensions/IListExtensionTests.cs +187 -1
  80. package/Tests/Runtime/Helper/ObjectsTests.cs +4 -4
  81. package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs +2923 -0
  82. package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs.meta +11 -0
  83. package/Tests/Runtime/Helper/ReflectionHelperTests.cs +660 -0
  84. package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +2 -2
  85. package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs +346 -0
  86. package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs.meta +11 -0
  87. package/Tests/Runtime/Performance/RandomPerformanceTests.cs +3 -0
  88. package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs +1238 -0
  89. package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs.meta +11 -0
  90. package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs +832 -0
  91. package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs.meta +11 -0
  92. package/Tests/Runtime/Random/FlurryBurstRandomTests.cs +12 -0
  93. package/Tests/Runtime/Random/FlurryBurstRandomTests.cs.meta +3 -0
  94. package/Tests/Runtime/Random/PhotonSpinRandomTests.cs +12 -0
  95. package/Tests/Runtime/Random/PhotonSpinRandomTests.cs.meta +3 -0
  96. package/Tests/Runtime/Random/RandomProtoSerializationTests.cs +14 -0
  97. package/Tests/Runtime/Random/RandomTestBase.cs +39 -4
  98. package/Tests/Runtime/Random/StormDropRandomTests.cs +12 -0
  99. package/Tests/Runtime/Random/StormDropRandomTests.cs.meta +3 -0
  100. package/Tests/Runtime/Serialization/ProtoInterfaceResolutionEdgeTests.cs +2 -2
  101. package/Tests/Runtime/Serialization/ProtoRootRegistrationTests.cs +1 -1
  102. package/Tests/Runtime/Serialization/ProtoSerializeBehaviorTests.cs +1 -1
  103. package/Tests/Runtime/Tags/PeriodicEffectDefinitionSerializationTests.cs +2 -2
  104. package/package.json +1 -1
  105. package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs +0 -60
  106. package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs.meta +0 -3
@@ -48,7 +48,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
48
48
  /// UnityEngine.Debug.Log(getter(p)); // 42
49
49
  /// ]]></code>
50
50
  /// </example>
51
- public static class ReflectionHelpers
51
+ public static partial class ReflectionHelpers
52
52
  {
53
53
  // Cache for type resolution by name
54
54
  #if !SINGLE_THREADED
@@ -66,54 +66,69 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
66
66
  private static readonly Dictionary<Type, Func<int, IList>> ListWithCapacityCreators = new();
67
67
  private static readonly Dictionary<Type, Func<int, object>> HashSetWithCapacityCreators =
68
68
  new();
69
- private static readonly Dictionary<FieldInfo, Func<object, object>> FieldGetterCache =
70
- new();
71
- private static readonly Dictionary<FieldInfo, Action<object, object>> FieldSetterCache =
72
- new();
73
- private static readonly Dictionary<FieldInfo, Func<object>> StaticFieldGetterCache = new();
74
- private static readonly Dictionary<FieldInfo, Action<object>> StaticFieldSetterCache =
75
- new();
76
- private static readonly Dictionary<PropertyInfo, Func<object, object>> PropertyGetterCache =
77
- new();
78
- private static readonly Dictionary<
79
- PropertyInfo,
80
- Action<object, object>
81
- > PropertySetterCache = new();
82
- private static readonly Dictionary<
83
- MethodInfo,
84
- Func<object, object[], object>
85
- > MethodInvokers = new();
86
- private static readonly Dictionary<
87
- MethodInfo,
88
- Func<object[], object>
89
- > StaticMethodInvokers = new();
90
- private static readonly Dictionary<ConstructorInfo, Func<object[], object>> Constructors =
91
- new();
69
+ private static readonly Dictionary<Type, Action<object>> HashSetClearers = new();
70
+
71
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
72
+ private static Func<int, Array> GetArrayCreatorCached(Type type)
73
+ {
74
+ if (!ArrayCreators.TryGetValue(type, out Func<int, Array> factory))
75
+ {
76
+ factory = GetArrayCreator(type);
77
+ ArrayCreators[type] = factory;
78
+ }
79
+
80
+ return factory;
81
+ }
82
+
83
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
84
+ private static Func<int, IList> GetListWithCapacityCreatorCached(Type elementType)
85
+ {
86
+ if (!ListWithCapacityCreators.TryGetValue(elementType, out Func<int, IList> factory))
87
+ {
88
+ factory = GetListWithCapacityCreator(elementType);
89
+ ListWithCapacityCreators[elementType] = factory;
90
+ }
91
+
92
+ return factory;
93
+ }
94
+
95
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
96
+ private static Func<IList> GetListCreatorCached(Type elementType)
97
+ {
98
+ if (!ListCreators.TryGetValue(elementType, out Func<IList> factory))
99
+ {
100
+ factory = GetListCreator(elementType);
101
+ ListCreators[elementType] = factory;
102
+ }
103
+
104
+ return factory;
105
+ }
106
+
107
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
108
+ private static Func<int, object> GetHashSetWithCapacityCreatorCached(Type elementType)
109
+ {
110
+ if (
111
+ !HashSetWithCapacityCreators.TryGetValue(elementType, out Func<int, object> factory)
112
+ )
113
+ {
114
+ factory = GetHashSetWithCapacityCreator(elementType);
115
+ HashSetWithCapacityCreators[elementType] = factory;
116
+ }
117
+
118
+ return factory;
119
+ }
120
+
121
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
122
+ private static Action<object> GetHashSetClearerCached(Type elementType)
123
+ {
124
+ if (!HashSetClearers.TryGetValue(elementType, out Action<object> clearer))
125
+ {
126
+ clearer = GetHashSetClearer(elementType);
127
+ HashSetClearers[elementType] = clearer;
128
+ }
92
129
 
93
- // Cache for typed static and instance invokers/actions in single-threaded mode
94
- private static readonly Dictionary<MethodInfo, Delegate> TypedStaticInvoker2 = new();
95
- private static readonly Dictionary<MethodInfo, Delegate> TypedStaticInvoker0 = new();
96
- private static readonly Dictionary<MethodInfo, Delegate> TypedStaticInvoker1 = new();
97
- private static readonly Dictionary<MethodInfo, Delegate> TypedStaticInvoker3 = new();
98
- private static readonly Dictionary<MethodInfo, Delegate> TypedStaticInvoker4 = new();
99
-
100
- private static readonly Dictionary<MethodInfo, Delegate> TypedStaticAction0 = new();
101
- private static readonly Dictionary<MethodInfo, Delegate> TypedStaticAction1 = new();
102
- private static readonly Dictionary<MethodInfo, Delegate> TypedStaticAction2 = new();
103
- private static readonly Dictionary<MethodInfo, Delegate> TypedStaticAction3 = new();
104
- private static readonly Dictionary<MethodInfo, Delegate> TypedStaticAction4 = new();
105
-
106
- private static readonly Dictionary<MethodInfo, Delegate> TypedInstanceInvoker0 = new();
107
- private static readonly Dictionary<MethodInfo, Delegate> TypedInstanceInvoker1 = new();
108
- private static readonly Dictionary<MethodInfo, Delegate> TypedInstanceInvoker2 = new();
109
- private static readonly Dictionary<MethodInfo, Delegate> TypedInstanceInvoker3 = new();
110
- private static readonly Dictionary<MethodInfo, Delegate> TypedInstanceInvoker4 = new();
111
-
112
- private static readonly Dictionary<MethodInfo, Delegate> TypedInstanceAction0 = new();
113
- private static readonly Dictionary<MethodInfo, Delegate> TypedInstanceAction1 = new();
114
- private static readonly Dictionary<MethodInfo, Delegate> TypedInstanceAction2 = new();
115
- private static readonly Dictionary<MethodInfo, Delegate> TypedInstanceAction3 = new();
116
- private static readonly Dictionary<MethodInfo, Delegate> TypedInstanceAction4 = new();
130
+ return clearer;
131
+ }
117
132
  #else
118
133
  private static readonly ConcurrentDictionary<Type, Func<int, Array>> ArrayCreators = new();
119
134
  private static readonly ConcurrentDictionary<Type, Func<IList>> ListCreators = new();
@@ -125,90 +140,91 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
125
140
  Type,
126
141
  Func<int, object>
127
142
  > HashSetWithCapacityCreators = new();
128
- private static readonly ConcurrentDictionary<
129
- FieldInfo,
130
- Func<object, object>
131
- > FieldGetterCache = new();
132
- private static readonly ConcurrentDictionary<
133
- FieldInfo,
134
- Action<object, object>
135
- > FieldSetterCache = new();
136
- private static readonly ConcurrentDictionary<
137
- FieldInfo,
138
- Func<object>
139
- > StaticFieldGetterCache = new();
140
- private static readonly ConcurrentDictionary<
141
- FieldInfo,
142
- Action<object>
143
- > StaticFieldSetterCache = new();
144
- private static readonly ConcurrentDictionary<
145
- PropertyInfo,
146
- Func<object, object>
147
- > PropertyGetterCache = new();
148
- private static readonly ConcurrentDictionary<
149
- PropertyInfo,
150
- Action<object, object>
151
- > PropertySetterCache = new();
152
- private static readonly ConcurrentDictionary<
153
- MethodInfo,
154
- Func<object, object[], object>
155
- > MethodInvokers = new();
156
- private static readonly ConcurrentDictionary<
157
- MethodInfo,
158
- Func<object[], object>
159
- > StaticMethodInvokers = new();
160
- private static readonly ConcurrentDictionary<
161
- ConstructorInfo,
162
- Func<object[], object>
163
- > Constructors = new();
143
+ private static readonly ConcurrentDictionary<Type, Action<object>> HashSetClearers = new();
164
144
 
165
- // Cache for typed static method invokers with 0-4 parameters to avoid object[] allocations
166
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedStaticInvoker2 =
167
- new();
168
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedStaticInvoker0 =
169
- new();
170
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedStaticInvoker1 =
171
- new();
172
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedStaticInvoker3 =
173
- new();
174
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedStaticInvoker4 =
175
- new();
145
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
146
+ private static Func<int, Array> GetArrayCreatorCached(Type type)
147
+ {
148
+ return ArrayCreators.GetOrAdd(type, static elementType => GetArrayCreator(elementType));
149
+ }
176
150
 
177
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedStaticAction0 =
178
- new();
179
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedStaticAction1 =
180
- new();
181
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedStaticAction2 =
182
- new();
183
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedStaticAction3 =
184
- new();
185
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedStaticAction4 =
186
- new();
151
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
152
+ private static Func<int, IList> GetListWithCapacityCreatorCached(Type elementType)
153
+ {
154
+ return ListWithCapacityCreators.GetOrAdd(
155
+ elementType,
156
+ static type => GetListWithCapacityCreator(type)
157
+ );
158
+ }
187
159
 
188
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedInstanceInvoker0 =
189
- new();
190
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedInstanceInvoker1 =
191
- new();
192
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedInstanceInvoker2 =
193
- new();
194
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedInstanceInvoker3 =
195
- new();
196
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedInstanceInvoker4 =
197
- new();
160
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
161
+ private static Func<IList> GetListCreatorCached(Type elementType)
162
+ {
163
+ return ListCreators.GetOrAdd(elementType, static type => GetListCreator(type));
164
+ }
198
165
 
199
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedInstanceAction0 =
200
- new();
201
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedInstanceAction1 =
202
- new();
203
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedInstanceAction2 =
204
- new();
205
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedInstanceAction3 =
206
- new();
207
- private static readonly ConcurrentDictionary<MethodInfo, Delegate> TypedInstanceAction4 =
208
- new();
166
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
167
+ private static Func<int, object> GetHashSetWithCapacityCreatorCached(Type elementType)
168
+ {
169
+ return HashSetWithCapacityCreators.GetOrAdd(
170
+ elementType,
171
+ static type => GetHashSetWithCapacityCreator(type)
172
+ );
173
+ }
174
+
175
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
176
+ private static Action<object> GetHashSetClearerCached(Type elementType)
177
+ {
178
+ return HashSetClearers.GetOrAdd(elementType, static type => GetHashSetClearer(type));
179
+ }
209
180
  #endif
210
181
 
211
182
  private static readonly bool CanCompileExpressions = CheckExpressionCompilationSupport();
183
+ private static readonly bool DynamicIlSupported = CheckDynamicIlSupport();
184
+ private static bool? _ExpressionCapabilityOverride;
185
+ private static bool? _DynamicIlCapabilityOverride;
186
+
187
+ internal static bool ExpressionsEnabled =>
188
+ _ExpressionCapabilityOverride ?? CanCompileExpressions;
189
+
190
+ internal static bool DynamicIlEnabled => _DynamicIlCapabilityOverride ?? DynamicIlSupported;
191
+
192
+ internal static IDisposable OverrideReflectionCapabilities(
193
+ bool? expressions,
194
+ bool? dynamicIl
195
+ )
196
+ {
197
+ bool? previousExpressions = _ExpressionCapabilityOverride;
198
+ bool? previousDynamicIl = _DynamicIlCapabilityOverride;
199
+ _ExpressionCapabilityOverride = expressions;
200
+ _DynamicIlCapabilityOverride = dynamicIl;
201
+ return new CapabilityOverrideScope(previousExpressions, previousDynamicIl);
202
+ }
203
+
204
+ private sealed class CapabilityOverrideScope : IDisposable
205
+ {
206
+ private readonly bool? _previousExpressions;
207
+ private readonly bool? _previousDynamicIl;
208
+ private bool _disposed;
209
+
210
+ internal CapabilityOverrideScope(bool? expressions, bool? dynamicIl)
211
+ {
212
+ _previousExpressions = expressions;
213
+ _previousDynamicIl = dynamicIl;
214
+ }
215
+
216
+ public void Dispose()
217
+ {
218
+ if (_disposed)
219
+ {
220
+ return;
221
+ }
222
+
223
+ _ExpressionCapabilityOverride = _previousExpressions;
224
+ _DynamicIlCapabilityOverride = _previousDynamicIl;
225
+ _disposed = true;
226
+ }
227
+ }
212
228
 
213
229
  #if SINGLE_THREADED
214
230
  private static readonly Dictionary<
@@ -335,7 +351,6 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
335
351
  );
336
352
  }
337
353
 
338
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
339
354
  /// <summary>
340
355
  /// Creates a new array instance of element <paramref name="type"/> with the specified length.
341
356
  /// </summary>
@@ -344,15 +359,12 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
344
359
  /// Array ints = ReflectionHelpers.CreateArray(typeof(int), 16); // int[16]
345
360
  /// ]]></code>
346
361
  /// </example>
362
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
347
363
  public static Array CreateArray(Type type, int length)
348
364
  {
349
- return ArrayCreators
350
- // ReSharper disable once ConvertClosureToMethodGroup
351
- .GetOrAdd(type, elementType => GetArrayCreator(elementType))
352
- .Invoke(length);
365
+ return GetArrayCreatorCached(type).Invoke(length);
353
366
  }
354
367
 
355
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
356
368
  /// <summary>
357
369
  /// Creates a new <see cref="List{T}"/> instance for <paramref name="elementType"/> with the specified capacity.
358
370
  /// </summary>
@@ -361,15 +373,12 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
361
373
  /// IList list = ReflectionHelpers.CreateList(typeof(string), 128); // List<string> with Capacity=128
362
374
  /// ]]></code>
363
375
  /// </example>
376
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
364
377
  public static IList CreateList(Type elementType, int length)
365
378
  {
366
- return ListWithCapacityCreators
367
- // ReSharper disable once ConvertClosureToMethodGroup
368
- .GetOrAdd(elementType, type => GetListWithCapacityCreator(type))
369
- .Invoke(length);
379
+ return GetListWithCapacityCreatorCached(elementType).Invoke(length);
370
380
  }
371
381
 
372
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
373
382
  /// <summary>
374
383
  /// Creates a new <see cref="List{T}"/> instance for <paramref name="elementType"/>.
375
384
  /// </summary>
@@ -378,21 +387,54 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
378
387
  /// IList list = ReflectionHelpers.CreateList(typeof(UnityEngine.Vector3));
379
388
  /// ]]></code>
380
389
  /// </example>
390
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
381
391
  public static IList CreateList(Type elementType)
382
392
  {
383
- // ReSharper disable once ConvertClosureToMethodGroup
384
- return ListCreators.GetOrAdd(elementType, type => GetListCreator(type)).Invoke();
393
+ return GetListCreatorCached(elementType).Invoke();
385
394
  }
386
395
 
387
396
  // Test helpers to avoid reflection in tests when asserting cache state
388
397
  internal static bool IsFieldGetterCached(FieldInfo field)
389
398
  {
390
- return field != null && FieldGetterCache.ContainsKey(field);
399
+ return DelegateFactory.IsFieldGetterCached(field);
391
400
  }
392
401
 
393
402
  internal static bool IsFieldSetterCached(FieldInfo field)
394
403
  {
395
- return field != null && FieldSetterCache.ContainsKey(field);
404
+ return DelegateFactory.IsFieldSetterCached(field);
405
+ }
406
+
407
+ internal static void ClearFieldGetterCache()
408
+ {
409
+ DelegateFactory.ClearFieldGetterCache();
410
+ }
411
+
412
+ internal static void ClearFieldSetterCache()
413
+ {
414
+ DelegateFactory.ClearFieldSetterCache();
415
+ }
416
+
417
+ internal static void ClearPropertyCache()
418
+ {
419
+ DelegateFactory.ClearPropertyCache();
420
+ }
421
+
422
+ internal static void ClearMethodCache()
423
+ {
424
+ DelegateFactory.ClearMethodCache();
425
+ }
426
+
427
+ internal static void ClearConstructorCache()
428
+ {
429
+ DelegateFactory.ClearConstructorCache();
430
+ }
431
+
432
+ internal static bool TryGetDelegateStrategy(
433
+ Delegate delegateInstance,
434
+ out ReflectionDelegateStrategy strategy
435
+ )
436
+ {
437
+ return DelegateFactory.TryGetStrategy(delegateInstance, out strategy);
396
438
  }
397
439
 
398
440
  /// <summary>
@@ -410,13 +452,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
410
452
  /// </example>
411
453
  public static Func<object, object> GetFieldGetter(FieldInfo field)
412
454
  {
413
- return FieldGetterCache.GetOrAdd(field, f =>
414
- #if !EMIT_DYNAMIC_IL
415
- CreateCompiledFieldGetter(f)
416
- #else
417
- BuildFieldGetterIL(f)
418
- #endif
419
- );
455
+ return DelegateFactory.GetFieldGetter(field);
420
456
  }
421
457
 
422
458
  #if EMIT_DYNAMIC_IL
@@ -465,13 +501,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
465
501
  /// </example>
466
502
  public static Func<object, object> GetPropertyGetter(PropertyInfo property)
467
503
  {
468
- return PropertyGetterCache.GetOrAdd(property, p =>
469
- #if !EMIT_DYNAMIC_IL
470
- CreateCompiledPropertyGetter(p)
471
- #else
472
- BuildPropertyGetterIL(p)
473
- #endif
474
- );
504
+ return DelegateFactory.GetPropertyGetter(property);
475
505
  }
476
506
 
477
507
  #if EMIT_DYNAMIC_IL
@@ -537,84 +567,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
537
567
  PropertyInfo property
538
568
  )
539
569
  {
540
- #if !EMIT_DYNAMIC_IL
541
- return Getter;
542
- TValue Getter(TInstance instance)
543
- {
544
- return (TValue)property.GetValue(instance);
545
- }
546
- #else
547
- MethodInfo getMethod = property.GetGetMethod(true);
548
- if (getMethod == null)
549
- {
550
- throw new ArgumentException(
551
- $"Property {property.Name} has no getter",
552
- nameof(property)
553
- );
554
- }
555
-
556
- DynamicMethod dynamicMethod = new(
557
- $"GetGeneric{property.DeclaringType.Name}_{property.Name}",
558
- typeof(TValue),
559
- new[] { typeof(TInstance) },
560
- property.DeclaringType,
561
- true
562
- );
563
-
564
- ILGenerator il = dynamicMethod.GetILGenerator();
565
-
566
- if (getMethod.IsStatic)
567
- {
568
- il.Emit(OpCodes.Call, getMethod);
569
- }
570
- else
571
- {
572
- if (typeof(TInstance).IsValueType)
573
- {
574
- il.Emit(OpCodes.Ldarga_S, 0);
575
- }
576
- else
577
- {
578
- il.Emit(OpCodes.Ldarg_0);
579
- }
580
-
581
- if (property.DeclaringType != typeof(TInstance))
582
- {
583
- il.Emit(
584
- property.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass,
585
- property.DeclaringType
586
- );
587
- }
588
-
589
- il.Emit(
590
- property.DeclaringType.IsValueType ? OpCodes.Call : OpCodes.Callvirt,
591
- getMethod
592
- );
593
- }
594
-
595
- if (property.PropertyType.IsValueType)
596
- {
597
- if (!typeof(TValue).IsValueType)
598
- {
599
- il.Emit(OpCodes.Box, property.PropertyType);
600
- }
601
- }
602
- else
603
- {
604
- if (typeof(TValue).IsValueType)
605
- {
606
- il.Emit(OpCodes.Unbox_Any, typeof(TValue));
607
- }
608
- else if (typeof(TValue) != property.PropertyType)
609
- {
610
- il.Emit(OpCodes.Castclass, typeof(TValue));
611
- }
612
- }
613
-
614
- il.Emit(OpCodes.Ret);
615
- return (Func<TInstance, TValue>)
616
- dynamicMethod.CreateDelegate(typeof(Func<TInstance, TValue>));
617
- #endif
570
+ return DelegateFactory.GetPropertyGetterTyped<TInstance, TValue>(property);
618
571
  }
619
572
 
620
573
  /// <summary>
@@ -641,13 +594,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
641
594
  nameof(property)
642
595
  );
643
596
  }
644
- return PropertySetterCache.GetOrAdd(property, p =>
645
- #if !EMIT_DYNAMIC_IL
646
- ((obj, value) => p.SetValue(obj, value))
647
- #else
648
- BuildPropertySetterIL(p)
649
- #endif
650
- );
597
+ return DelegateFactory.GetPropertySetter(property);
651
598
  }
652
599
 
653
600
  public static Action<TInstance, TValue> GetPropertySetter<TInstance, TValue>(
@@ -662,106 +609,25 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
662
609
  nameof(property)
663
610
  );
664
611
  }
665
- #if !EMIT_DYNAMIC_IL
666
- return (instance, value) => property.SetValue(instance, value);
667
- #else
668
- DynamicMethod dynamicMethod = new(
669
- $"SetPropertyGeneric{property.DeclaringType.Name}_{property.Name}",
670
- typeof(void),
671
- new[] { typeof(TInstance), typeof(TValue) },
672
- property.DeclaringType,
673
- true
674
- );
675
- ILGenerator il = dynamicMethod.GetILGenerator();
612
+ return DelegateFactory.GetPropertySetterTyped<TInstance, TValue>(property);
613
+ }
676
614
 
677
- if (setMethod.IsStatic)
615
+ public static Action<TValue> GetStaticPropertySetter<TValue>(PropertyInfo property)
616
+ {
617
+ MethodInfo setMethod = property.GetSetMethod(true);
618
+ if (setMethod == null || !setMethod.IsStatic)
678
619
  {
679
- il.Emit(OpCodes.Ldarg_1);
680
- if (property.PropertyType != typeof(TValue))
681
- {
682
- il.Emit(
683
- property.PropertyType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass,
684
- property.PropertyType
685
- );
686
- }
687
- il.Emit(OpCodes.Call, setMethod);
620
+ throw new ArgumentException(
621
+ $"Property {property?.Name} must be static and have a setter",
622
+ nameof(property)
623
+ );
688
624
  }
689
- else
690
- {
691
- if (typeof(TInstance).IsValueType)
692
- {
693
- il.Emit(OpCodes.Ldarga_S, 0);
694
- }
695
- else
696
- {
697
- il.Emit(OpCodes.Ldarg_0);
698
- }
699
- if (property.DeclaringType != typeof(TInstance))
700
- {
701
- il.Emit(
702
- property.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass,
703
- property.DeclaringType
704
- );
705
- }
706
-
707
- il.Emit(OpCodes.Ldarg_1);
708
- if (property.PropertyType != typeof(TValue))
709
- {
710
- il.Emit(
711
- property.PropertyType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass,
712
- property.PropertyType
713
- );
714
- }
715
-
716
- il.Emit(
717
- property.DeclaringType.IsValueType ? OpCodes.Call : OpCodes.Callvirt,
718
- setMethod
719
- );
720
- }
721
- il.Emit(OpCodes.Ret);
722
- return (Action<TInstance, TValue>)
723
- dynamicMethod.CreateDelegate(typeof(Action<TInstance, TValue>));
724
- #endif
725
- }
726
-
727
- public static Action<TValue> GetStaticPropertySetter<TValue>(PropertyInfo property)
728
- {
729
- MethodInfo setMethod = property.GetSetMethod(true);
730
- if (setMethod == null || !setMethod.IsStatic)
731
- {
732
- throw new ArgumentException(
733
- $"Property {property?.Name} must be static and have a setter",
734
- nameof(property)
735
- );
736
- }
737
- #if !EMIT_DYNAMIC_IL
738
- return value => property.SetValue(null, value);
739
- #else
740
- DynamicMethod dynamicMethod = new(
741
- $"SetStaticProperty_{property.DeclaringType.Name}_{property.Name}",
742
- typeof(void),
743
- new[] { typeof(TValue) },
744
- property.DeclaringType,
745
- true
746
- );
747
- ILGenerator il = dynamicMethod.GetILGenerator();
748
- il.Emit(OpCodes.Ldarg_0);
749
- if (property.PropertyType != typeof(TValue))
750
- {
751
- il.Emit(
752
- property.PropertyType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass,
753
- property.PropertyType
754
- );
755
- }
756
- il.Emit(OpCodes.Call, setMethod);
757
- il.Emit(OpCodes.Ret);
758
- return (Action<TValue>)dynamicMethod.CreateDelegate(typeof(Action<TValue>));
759
- #endif
760
- }
761
-
762
- public static Func<object, object[], object> GetIndexerGetter(PropertyInfo property)
763
- {
764
- if (property == null)
625
+ return DelegateFactory.GetStaticPropertySetterTyped<TValue>(property);
626
+ }
627
+
628
+ public static Func<object, object[], object> GetIndexerGetter(PropertyInfo property)
629
+ {
630
+ if (property == null)
765
631
  {
766
632
  throw new ArgumentNullException(nameof(property));
767
633
  }
@@ -770,80 +636,12 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
770
636
  {
771
637
  throw new ArgumentException("Property is not an indexer", nameof(property));
772
638
  }
773
- #if !EMIT_DYNAMIC_IL
774
- if (!CanCompileExpressions)
775
- {
776
- return (instance, indexArgs) =>
777
- {
778
- if (indexArgs == null || indexArgs.Length != indices.Length)
779
- {
780
- throw new IndexOutOfRangeException(
781
- $"Indexer expects {indices.Length} index argument(s); received {(indexArgs == null ? 0 : indexArgs.Length)}."
782
- );
783
- }
784
- return property.GetValue(instance, indexArgs);
785
- };
786
- }
787
- ParameterExpression inst = Expression.Parameter(typeof(object), "instance");
788
- ParameterExpression args = Expression.Parameter(typeof(object[]), "args");
789
- Expression target = property.DeclaringType.IsValueType
790
- ? Expression.Unbox(inst, property.DeclaringType)
791
- : Expression.Convert(inst, property.DeclaringType);
792
- UnaryExpression[] indexExprs = indices
793
- .Select(
794
- (p, i) =>
795
- p.ParameterType.IsValueType
796
- ? Expression.Unbox(
797
- Expression.ArrayIndex(args, Expression.Constant(i)),
798
- p.ParameterType
799
- )
800
- : Expression.Convert(
801
- Expression.ArrayIndex(args, Expression.Constant(i)),
802
- p.ParameterType
803
- )
804
- )
805
- .ToArray();
806
- Expression call = Expression.Property(target, property, indexExprs);
807
- Expression ret = property.PropertyType.IsValueType
808
- ? Expression.Convert(call, typeof(object))
809
- : call;
810
- return Expression.Lambda<Func<object, object[], object>>(ret, inst, args).Compile();
811
- #else
812
639
  MethodInfo getter = property.GetGetMethod(true);
813
640
  if (getter == null)
814
641
  {
815
642
  throw new ArgumentException("Indexer has no getter", nameof(property));
816
643
  }
817
- DynamicMethod dm = new(
818
- $"GetIndexer_{property.DeclaringType.Name}_{property.Name}",
819
- typeof(object),
820
- new[] { typeof(object), typeof(object[]) },
821
- property.DeclaringType,
822
- true
823
- );
824
- ILGenerator il = dm.GetILGenerator();
825
- il.Emit(OpCodes.Ldarg_0);
826
- il.Emit(
827
- property.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass,
828
- property.DeclaringType
829
- );
830
- for (int i = 0; i < indices.Length; i++)
831
- {
832
- il.Emit(OpCodes.Ldarg_1);
833
- il.Emit(OpCodes.Ldc_I4, i);
834
- il.Emit(OpCodes.Ldelem_Ref);
835
- Type pt = indices[i].ParameterType;
836
- il.Emit(pt.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, pt);
837
- }
838
- il.Emit(property.DeclaringType.IsValueType ? OpCodes.Call : OpCodes.Callvirt, getter);
839
- if (property.PropertyType.IsValueType)
840
- {
841
- il.Emit(OpCodes.Box, property.PropertyType);
842
- }
843
- il.Emit(OpCodes.Ret);
844
- return (Func<object, object[], object>)
845
- dm.CreateDelegate(typeof(Func<object, object[], object>));
846
- #endif
644
+ return DelegateFactory.GetIndexerGetter(property);
847
645
  }
848
646
 
849
647
  public static Action<object, object, object[]> GetIndexerSetter(PropertyInfo property)
@@ -862,100 +660,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
862
660
  {
863
661
  throw new ArgumentException("Indexer has no setter", nameof(property));
864
662
  }
865
- #if !EMIT_DYNAMIC_IL
866
- if (!CanCompileExpressions)
867
- {
868
- return (instance, value, indexArgs) =>
869
- {
870
- if (indexArgs == null || indexArgs.Length != indices.Length)
871
- {
872
- throw new IndexOutOfRangeException(
873
- $"Indexer expects {indices.Length} index argument(s); received {(indexArgs == null ? 0 : indexArgs.Length)}."
874
- );
875
- }
876
- // Validate index argument types to mirror IL/unbox behavior
877
- for (int i = 0; i < indices.Length; i++)
878
- {
879
- object idxVal = indexArgs[i];
880
- Type pt = indices[i].ParameterType;
881
- if (idxVal == null)
882
- {
883
- if (pt.IsValueType && Nullable.GetUnderlyingType(pt) == null)
884
- {
885
- throw new InvalidCastException(
886
- $"Object of type 'null' cannot be converted to type '{pt}'."
887
- );
888
- }
889
- }
890
- else if (!pt.IsInstanceOfType(idxVal))
891
- {
892
- throw new InvalidCastException(
893
- $"Object of type '{idxVal.GetType()}' cannot be converted to type '{pt}'."
894
- );
895
- }
896
- }
897
- property.SetValue(instance, value, indexArgs);
898
- };
899
- }
900
- ParameterExpression inst = Expression.Parameter(typeof(object), "instance");
901
- ParameterExpression value = Expression.Parameter(typeof(object), "value");
902
- ParameterExpression args = Expression.Parameter(typeof(object[]), "args");
903
- Expression target = property.DeclaringType.IsValueType
904
- ? Expression.Unbox(inst, property.DeclaringType)
905
- : Expression.Convert(inst, property.DeclaringType);
906
- UnaryExpression[] indexExprs = indices
907
- .Select(
908
- (p, i) =>
909
- p.ParameterType.IsValueType
910
- ? Expression.Unbox(
911
- Expression.ArrayIndex(args, Expression.Constant(i)),
912
- p.ParameterType
913
- )
914
- : Expression.Convert(
915
- Expression.ArrayIndex(args, Expression.Constant(i)),
916
- p.ParameterType
917
- )
918
- )
919
- .ToArray();
920
- Expression valExpr = property.PropertyType.IsValueType
921
- ? Expression.Unbox(value, property.PropertyType)
922
- : Expression.Convert(value, property.PropertyType);
923
- Expression call = Expression.Call(target, setter, indexExprs.Append(valExpr));
924
- return Expression
925
- .Lambda<Action<object, object, object[]>>(call, inst, value, args)
926
- .Compile();
927
- #else
928
- DynamicMethod dm = new(
929
- $"SetIndexer_{property.DeclaringType.Name}_{property.Name}",
930
- typeof(void),
931
- new[] { typeof(object), typeof(object), typeof(object[]) },
932
- property.DeclaringType,
933
- true
934
- );
935
- ILGenerator il = dm.GetILGenerator();
936
- il.Emit(OpCodes.Ldarg_0);
937
- il.Emit(
938
- property.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass,
939
- property.DeclaringType
940
- );
941
- for (int i = 0; i < indices.Length; i++)
942
- {
943
- il.Emit(OpCodes.Ldarg_2);
944
- il.Emit(OpCodes.Ldc_I4, i);
945
- il.Emit(OpCodes.Ldelem_Ref);
946
- Type pt = indices[i].ParameterType;
947
- il.Emit(pt.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, pt);
948
- }
949
- il.Emit(OpCodes.Ldarg_1);
950
- il.Emit(
951
- property.PropertyType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass,
952
- property.PropertyType
953
- );
954
- il.Emit(property.DeclaringType.IsValueType ? OpCodes.Call : OpCodes.Callvirt, setter);
955
- il.Emit(OpCodes.Ret);
956
- return (Action<object, object, object[]>)
957
- dm.CreateDelegate(typeof(Action<object, object, object[]>));
958
- #endif
663
+ return DelegateFactory.GetIndexerSetter(property);
959
664
  }
960
665
 
961
666
  #if EMIT_DYNAMIC_IL
@@ -1033,25 +738,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1033
738
  $"Type {type.FullName} does not have a parameterless constructor"
1034
739
  );
1035
740
  }
1036
- #if !EMIT_DYNAMIC_IL
1037
- return () => Activator.CreateInstance(type);
1038
- #else
1039
- DynamicMethod dynamicMethod = new(
1040
- $"New_{type.Name}",
1041
- typeof(object),
1042
- Type.EmptyTypes,
1043
- type,
1044
- true
1045
- );
1046
- ILGenerator il = dynamicMethod.GetILGenerator();
1047
- il.Emit(OpCodes.Newobj, ctor);
1048
- if (type.IsValueType)
1049
- {
1050
- il.Emit(OpCodes.Box, type);
1051
- }
1052
- il.Emit(OpCodes.Ret);
1053
- return (Func<object>)dynamicMethod.CreateDelegate(typeof(Func<object>));
1054
- #endif
741
+ return DelegateFactory.GetParameterlessConstructor(ctor);
1055
742
  }
1056
743
 
1057
744
  /// <summary>
@@ -1066,13 +753,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1066
753
  {
1067
754
  throw new ArgumentException(nameof(field));
1068
755
  }
1069
- return StaticFieldGetterCache.GetOrAdd(field, f =>
1070
- #if !EMIT_DYNAMIC_IL
1071
- CreateCompiledStaticFieldGetter(f)
1072
- #else
1073
- BuildStaticFieldGetterIL(f)
1074
- #endif
1075
- );
756
+ return DelegateFactory.GetStaticFieldGetter(field);
1076
757
  }
1077
758
 
1078
759
  #if EMIT_DYNAMIC_IL
@@ -1101,72 +782,11 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1101
782
  /// </summary>
1102
783
  public static Func<TInstance, TValue> GetFieldGetter<TInstance, TValue>(FieldInfo field)
1103
784
  {
1104
- #if !EMIT_DYNAMIC_IL
1105
- return Getter;
1106
- TValue Getter(TInstance instance)
1107
- {
1108
- return (TValue)field.GetValue(instance);
1109
- }
1110
- #else
1111
- DynamicMethod dynamicMethod = new(
1112
- $"GetGeneric{field.DeclaringType.Name}_{field.Name}",
1113
- typeof(TValue),
1114
- new[] { typeof(TInstance) },
1115
- field.DeclaringType,
1116
- true
1117
- );
1118
-
1119
- ILGenerator il = dynamicMethod.GetILGenerator();
1120
-
1121
- if (!field.IsStatic)
1122
- {
1123
- if (typeof(TInstance).IsValueType)
1124
- {
1125
- il.Emit(OpCodes.Ldarga_S, 0);
1126
- }
1127
- else
1128
- {
1129
- il.Emit(OpCodes.Ldarg_0);
1130
- }
1131
-
1132
- if (field.DeclaringType != typeof(TInstance))
1133
- {
1134
- il.Emit(
1135
- field.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass,
1136
- field.DeclaringType
1137
- );
1138
- }
1139
-
1140
- il.Emit(OpCodes.Ldfld, field);
1141
- }
1142
- else
1143
- {
1144
- il.Emit(OpCodes.Ldsfld, field);
1145
- }
1146
-
1147
- if (field.FieldType.IsValueType)
1148
- {
1149
- if (!typeof(TValue).IsValueType)
1150
- {
1151
- il.Emit(OpCodes.Box, field.FieldType);
1152
- }
1153
- }
1154
- else
785
+ if (field == null)
1155
786
  {
1156
- if (typeof(TValue).IsValueType)
1157
- {
1158
- il.Emit(OpCodes.Unbox_Any, typeof(TValue));
1159
- }
1160
- else if (typeof(TValue) != field.FieldType)
1161
- {
1162
- il.Emit(OpCodes.Castclass, typeof(TValue));
1163
- }
787
+ throw new ArgumentNullException(nameof(field));
1164
788
  }
1165
-
1166
- il.Emit(OpCodes.Ret);
1167
- return (Func<TInstance, TValue>)
1168
- dynamicMethod.CreateDelegate(typeof(Func<TInstance, TValue>));
1169
- #endif
789
+ return DelegateFactory.GetFieldGetterTyped<TInstance, TValue>(field);
1170
790
  }
1171
791
 
1172
792
  /// <summary>
@@ -1180,52 +800,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1180
800
  /// <returns>Delegate: <c>() =&gt; TValue</c></returns>
1181
801
  public static Func<TValue> GetStaticPropertyGetter<TValue>(PropertyInfo property)
1182
802
  {
1183
- MethodInfo getMethod = property.GetGetMethod(true);
1184
- #if !EMIT_DYNAMIC_IL
1185
- return Getter;
1186
- TValue Getter()
1187
- {
1188
- // Use null for instance, null for indexer args for static properties
1189
- return (TValue)property.GetValue(null, null);
1190
- }
1191
- #else
1192
- DynamicMethod dynamicMethod = new(
1193
- $"GetStatic_{property.DeclaringType.Name}_{property.Name}",
1194
- typeof(TValue),
1195
- Type.EmptyTypes,
1196
- property.DeclaringType,
1197
- true
1198
- );
1199
-
1200
- ILGenerator il = dynamicMethod.GetILGenerator();
1201
-
1202
- il.Emit(OpCodes.Call, getMethod);
1203
-
1204
- Type actualType = property.PropertyType;
1205
- Type targetType = typeof(TValue);
1206
-
1207
- if (actualType != targetType)
1208
- {
1209
- if (actualType.IsValueType)
1210
- {
1211
- il.Emit(OpCodes.Box, actualType);
1212
- if (targetType != typeof(object))
1213
- {
1214
- il.Emit(OpCodes.Castclass, targetType);
1215
- }
1216
- }
1217
- else
1218
- {
1219
- il.Emit(
1220
- targetType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass,
1221
- targetType
1222
- );
1223
- }
1224
- }
1225
-
1226
- il.Emit(OpCodes.Ret);
1227
- return (Func<TValue>)dynamicMethod.CreateDelegate(typeof(Func<TValue>));
1228
- #endif
803
+ return DelegateFactory.GetStaticPropertyGetterTyped<TValue>(property);
1229
804
  }
1230
805
 
1231
806
  /// <summary>
@@ -1244,52 +819,8 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1244
819
  {
1245
820
  throw new ArgumentException(nameof(field));
1246
821
  }
1247
-
1248
- #if !EMIT_DYNAMIC_IL
1249
- return Getter;
1250
- TValue Getter()
1251
- {
1252
- return (TValue)field.GetValue(null);
1253
- }
1254
- #else
1255
- DynamicMethod dynamicMethod = new(
1256
- $"GetStatic_{field.DeclaringType.Name}_{field.Name}",
1257
- typeof(TValue),
1258
- Type.EmptyTypes,
1259
- field.DeclaringType,
1260
- true
1261
- );
1262
-
1263
- ILGenerator il = dynamicMethod.GetILGenerator();
1264
-
1265
- il.Emit(OpCodes.Ldsfld, field);
1266
-
1267
- Type actualType = field.FieldType;
1268
- Type targetType = typeof(TValue);
1269
-
1270
- if (actualType != targetType)
1271
- {
1272
- if (actualType.IsValueType)
1273
- {
1274
- il.Emit(OpCodes.Box, actualType);
1275
- if (targetType != typeof(object))
1276
- {
1277
- il.Emit(OpCodes.Castclass, targetType);
1278
- }
1279
- }
1280
- else
1281
- {
1282
- il.Emit(
1283
- targetType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass,
1284
- targetType
1285
- );
1286
- }
1287
- }
1288
-
1289
- il.Emit(OpCodes.Ret);
1290
- return (Func<TValue>)dynamicMethod.CreateDelegate(typeof(Func<TValue>));
1291
- #endif
1292
- }
822
+ return DelegateFactory.GetStaticFieldGetterTyped<TValue>(field);
823
+ }
1293
824
 
1294
825
  /// <summary>
1295
826
  /// Builds a strongly-typed field setter for instance fields.
@@ -1298,42 +829,11 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1298
829
  FieldInfo field
1299
830
  )
1300
831
  {
1301
- #if !EMIT_DYNAMIC_IL
1302
- return Setter;
1303
- void Setter(ref TInstance instance, TValue newValue)
1304
- {
1305
- object value = instance;
1306
- field.SetValue(value, newValue);
1307
- instance = (TInstance)value;
1308
- }
1309
- #else
1310
- Type instanceType = field.DeclaringType;
1311
- Type valueType = field.FieldType;
1312
-
1313
- DynamicMethod dynamicMethod = new(
1314
- $"SetFieldGeneric{field.DeclaringType.Name}_{field.Name}",
1315
- MethodAttributes.Public | MethodAttributes.Static,
1316
- CallingConventions.Standard,
1317
- typeof(void),
1318
- new[] { instanceType.MakeByRefType(), valueType },
1319
- field.Module,
1320
- true
1321
- );
1322
-
1323
- ILGenerator il = dynamicMethod.GetILGenerator();
1324
- il.Emit(OpCodes.Ldarg_0);
1325
- if (!instanceType.IsValueType)
832
+ if (field == null)
1326
833
  {
1327
- il.Emit(OpCodes.Ldind_Ref);
834
+ throw new ArgumentNullException(nameof(field));
1328
835
  }
1329
-
1330
- il.Emit(OpCodes.Ldarg_1);
1331
- il.Emit(OpCodes.Stfld, field);
1332
- il.Emit(OpCodes.Ret);
1333
-
1334
- Type delegateType = typeof(FieldSetter<,>).MakeGenericType(instanceType, valueType);
1335
- return (FieldSetter<TInstance, TValue>)dynamicMethod.CreateDelegate(delegateType);
1336
- #endif
836
+ return DelegateFactory.GetFieldSetterTyped<TInstance, TValue>(field);
1337
837
  }
1338
838
 
1339
839
  /// <summary>
@@ -1352,28 +852,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1352
852
  {
1353
853
  throw new ArgumentException(nameof(field));
1354
854
  }
1355
- #if !EMIT_DYNAMIC_IL
1356
- return Setter;
1357
- void Setter(TValue newValue)
1358
- {
1359
- field.SetValue(null, newValue);
1360
- }
1361
- #else
1362
- DynamicMethod dynamicMethod = new(
1363
- $"SetFieldGenericStatic{field.DeclaringType.Name}_{field.Name}",
1364
- typeof(void),
1365
- new[] { typeof(TValue) },
1366
- field.Module,
1367
- true
1368
- );
1369
-
1370
- ILGenerator il = dynamicMethod.GetILGenerator();
1371
- il.Emit(OpCodes.Ldarg_0);
1372
- il.Emit(OpCodes.Stsfld, field);
1373
- il.Emit(OpCodes.Ret);
1374
-
1375
- return (Action<TValue>)dynamicMethod.CreateDelegate(typeof(Action<TValue>));
1376
- #endif
855
+ return DelegateFactory.GetStaticFieldSetterTyped<TValue>(field);
1377
856
  }
1378
857
 
1379
858
  /// <summary>
@@ -1387,13 +866,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1387
866
  /// <returns>Delegate: <c>(object instance, object value) =&gt; void</c></returns>
1388
867
  public static Action<object, object> GetFieldSetter(FieldInfo field)
1389
868
  {
1390
- return FieldSetterCache.GetOrAdd(field, f =>
1391
- #if !EMIT_DYNAMIC_IL
1392
- CreateCompiledFieldSetter(f)
1393
- #else
1394
- BuildFieldSetterIL(f)
1395
- #endif
1396
- );
869
+ return DelegateFactory.GetFieldSetter(field);
1397
870
  }
1398
871
 
1399
872
  #if EMIT_DYNAMIC_IL
@@ -1439,13 +912,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1439
912
  {
1440
913
  throw new ArgumentException(nameof(field));
1441
914
  }
1442
- return StaticFieldSetterCache.GetOrAdd(field, f =>
1443
- #if !EMIT_DYNAMIC_IL
1444
- CreateCompiledStaticFieldSetter(f)
1445
- #else
1446
- BuildStaticFieldSetterIL(f)
1447
- #endif
1448
- );
915
+ return DelegateFactory.GetStaticFieldSetter(field);
1449
916
  }
1450
917
 
1451
918
  #if EMIT_DYNAMIC_IL
@@ -1579,7 +1046,6 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1579
1046
  #endif
1580
1047
  }
1581
1048
 
1582
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
1583
1049
  /// <summary>
1584
1050
  /// Creates a <see cref="HashSet{T}"/> instance for the given element type with capacity.
1585
1051
  /// </summary>
@@ -1591,12 +1057,10 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1591
1057
  /// object set = ReflectionHelpers.CreateHashSet(typeof(string), 64); // HashSet<string>
1592
1058
  /// ]]></code>
1593
1059
  /// </example>
1060
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
1594
1061
  public static object CreateHashSet(Type elementType, int capacity)
1595
1062
  {
1596
- return HashSetWithCapacityCreators
1597
- // ReSharper disable once ConvertClosureToMethodGroup
1598
- .GetOrAdd(elementType, type => GetHashSetWithCapacityCreator(type))
1599
- .Invoke(capacity);
1063
+ return GetHashSetWithCapacityCreatorCached(elementType).Invoke(capacity);
1600
1064
  }
1601
1065
 
1602
1066
  /// <summary>
@@ -1724,6 +1188,48 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1724
1188
  #endif
1725
1189
  }
1726
1190
 
1191
+ public static Action<object> GetHashSetClearer(Type elementType)
1192
+ {
1193
+ if (elementType == null)
1194
+ {
1195
+ throw new ArgumentNullException(nameof(elementType));
1196
+ }
1197
+
1198
+ return HashSetClearers.GetOrAdd(
1199
+ elementType,
1200
+ static type =>
1201
+ {
1202
+ Type hashSetType = typeof(HashSet<>).MakeGenericType(type);
1203
+ MethodInfo clearMethod = hashSetType.GetMethod("Clear", Type.EmptyTypes);
1204
+ if (clearMethod == null)
1205
+ {
1206
+ return _ => { };
1207
+ }
1208
+ #if SUPPORT_EXPRESSION_COMPILE
1209
+ if (ExpressionsEnabled)
1210
+ {
1211
+ try
1212
+ {
1213
+ ParameterExpression target = Expression.Parameter(
1214
+ typeof(object),
1215
+ "set"
1216
+ );
1217
+ UnaryExpression cast = Expression.Convert(target, hashSetType);
1218
+ MethodCallExpression call = Expression.Call(cast, clearMethod);
1219
+ return Expression.Lambda<Action<object>>(call, target).Compile();
1220
+ }
1221
+ catch
1222
+ {
1223
+ // Fall through to reflection fallback
1224
+ }
1225
+ }
1226
+ #endif
1227
+ MethodInfo closedMethod = clearMethod;
1228
+ return set => closedMethod.Invoke(set, Array.Empty<object>());
1229
+ }
1230
+ );
1231
+ }
1232
+
1727
1233
  /// <summary>
1728
1234
  /// Invokes an instance method using a cached invoker; avoids per-call reflection overhead.
1729
1235
  /// </summary>
@@ -1732,22 +1238,13 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1732
1238
  /// <param name="parameters">Optional parameters.</param>
1733
1239
  /// <returns>The return value from the method, or null for void.</returns>
1734
1240
  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1735
- /// <summary>
1736
- /// Invokes an instance method using a cached delegate when possible.
1737
- /// </summary>
1738
- /// <param name="method">Instance method to invoke.</param>
1739
- /// <param name="instance">Target instance.</param>
1740
- /// <param name="parameters">Method parameters (optional).</param>
1741
- /// <returns>Boxed return value or null for void methods.</returns>
1742
1241
  public static object InvokeMethod(
1743
1242
  MethodInfo method,
1744
1243
  object instance,
1745
1244
  params object[] parameters
1746
1245
  )
1747
1246
  {
1748
- return MethodInvokers
1749
- .GetOrAdd(method, methodInfo => GetMethodInvoker(methodInfo))
1750
- .Invoke(instance, parameters);
1247
+ return DelegateFactory.GetMethodInvoker(method).Invoke(instance, parameters);
1751
1248
  }
1752
1249
 
1753
1250
  /// <summary>
@@ -1757,40 +1254,23 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1757
1254
  /// <param name="parameters">Optional parameters.</param>
1758
1255
  /// <returns>The return value from the method, or null for void.</returns>
1759
1256
  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1760
- /// <summary>
1761
- /// Invokes a static method using a cached delegate when possible.
1762
- /// </summary>
1763
- /// <param name="method">Static method.</param>
1764
- /// <param name="parameters">Method parameters.</param>
1765
- /// <returns>Boxed return value or null for void methods.</returns>
1766
1257
  public static object InvokeStaticMethod(MethodInfo method, params object[] parameters)
1767
1258
  {
1768
- return StaticMethodInvokers
1769
- .GetOrAdd(method, methodInfo => GetStaticMethodInvoker(methodInfo))
1770
- .Invoke(parameters);
1259
+ return DelegateFactory.GetStaticMethodInvoker(method).Invoke(parameters);
1771
1260
  }
1772
1261
 
1773
- /// <summary>
1774
- /// Constructs an instance using a cached constructor invoker.
1775
- /// </summary>
1776
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
1777
1262
  /// <summary>
1778
1263
  /// Creates an instance using a constructor via a cached delegate.
1779
1264
  /// </summary>
1780
1265
  /// <param name="constructor">Constructor to invoke.</param>
1781
1266
  /// <param name="parameters">Constructor parameters.</param>
1782
1267
  /// <returns>Created instance.</returns>
1268
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
1783
1269
  public static object CreateInstance(ConstructorInfo constructor, params object[] parameters)
1784
1270
  {
1785
- return Constructors
1786
- .GetOrAdd(constructor, ctor => GetConstructor(ctor))
1787
- .Invoke(parameters);
1271
+ return DelegateFactory.GetConstructorInvoker(constructor).Invoke(parameters);
1788
1272
  }
1789
1273
 
1790
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
1791
- /// <summary>
1792
- /// Constructs an instance using a cached constructor invoker and returns it as T.
1793
- /// </summary>
1794
1274
  /// <summary>
1795
1275
  /// Constructs an instance using a cached constructor invoker and returns it as T.
1796
1276
  /// </summary>
@@ -1805,6 +1285,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1805
1285
  /// var p = ReflectionHelpers.CreateInstance<Player>(name, level);
1806
1286
  /// ]]></code>
1807
1287
  /// </example>
1288
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
1808
1289
  public static T CreateInstance<T>(params object[] parameters)
1809
1290
  {
1810
1291
  Type type = typeof(T);
@@ -1853,60 +1334,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1853
1334
  nameof(method)
1854
1335
  );
1855
1336
  }
1856
-
1857
- #if !EMIT_DYNAMIC_IL
1858
- return CreateCompiledMethodInvoker(method);
1859
- #else
1860
- DynamicMethod dynamicMethod = new(
1861
- $"Invoke{method.DeclaringType.Name}_{method.Name}",
1862
- typeof(object),
1863
- new[] { typeof(object), typeof(object[]) },
1864
- method.DeclaringType,
1865
- true
1866
- );
1867
-
1868
- ILGenerator il = dynamicMethod.GetILGenerator();
1869
- ParameterInfo[] parameters = method.GetParameters();
1870
-
1871
- il.Emit(OpCodes.Ldarg_0);
1872
- il.Emit(
1873
- method.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass,
1874
- method.DeclaringType
1875
- );
1876
-
1877
- for (int i = 0; i < parameters.Length; i++)
1878
- {
1879
- il.Emit(OpCodes.Ldarg_1);
1880
- il.Emit(OpCodes.Ldc_I4, i);
1881
- il.Emit(OpCodes.Ldelem_Ref);
1882
-
1883
- Type paramType = parameters[i].ParameterType;
1884
- if (paramType.IsValueType)
1885
- {
1886
- il.Emit(OpCodes.Unbox_Any, paramType);
1887
- }
1888
- else
1889
- {
1890
- il.Emit(OpCodes.Castclass, paramType);
1891
- }
1892
- }
1893
-
1894
- il.Emit(method.DeclaringType.IsValueType ? OpCodes.Call : OpCodes.Callvirt, method);
1895
-
1896
- if (method.ReturnType == typeof(void))
1897
- {
1898
- il.Emit(OpCodes.Ldnull);
1899
- }
1900
- else if (method.ReturnType.IsValueType)
1901
- {
1902
- il.Emit(OpCodes.Box, method.ReturnType);
1903
- }
1904
-
1905
- il.Emit(OpCodes.Ret);
1906
-
1907
- return (Func<object, object[], object>)
1908
- dynamicMethod.CreateDelegate(typeof(Func<object, object[], object>));
1909
- #endif
1337
+ return DelegateFactory.GetMethodInvoker(method);
1910
1338
  }
1911
1339
 
1912
1340
  /// <summary>
@@ -1918,54 +1346,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1918
1346
  {
1919
1347
  throw new ArgumentException("Method must be static", nameof(method));
1920
1348
  }
1921
-
1922
- #if !EMIT_DYNAMIC_IL
1923
- return CreateCompiledStaticMethodInvoker(method);
1924
- #else
1925
- DynamicMethod dynamicMethod = new(
1926
- $"InvokeStatic{method.DeclaringType.Name}_{method.Name}",
1927
- typeof(object),
1928
- new[] { typeof(object[]) },
1929
- method.DeclaringType,
1930
- true
1931
- );
1932
-
1933
- ILGenerator il = dynamicMethod.GetILGenerator();
1934
- ParameterInfo[] parameters = method.GetParameters();
1935
-
1936
- for (int i = 0; i < parameters.Length; i++)
1937
- {
1938
- il.Emit(OpCodes.Ldarg_0);
1939
- il.Emit(OpCodes.Ldc_I4, i);
1940
- il.Emit(OpCodes.Ldelem_Ref);
1941
-
1942
- Type paramType = parameters[i].ParameterType;
1943
- if (paramType.IsValueType)
1944
- {
1945
- il.Emit(OpCodes.Unbox_Any, paramType);
1946
- }
1947
- else
1948
- {
1949
- il.Emit(OpCodes.Castclass, paramType);
1950
- }
1951
- }
1952
-
1953
- il.Emit(OpCodes.Call, method);
1954
-
1955
- if (method.ReturnType == typeof(void))
1956
- {
1957
- il.Emit(OpCodes.Ldnull);
1958
- }
1959
- else if (method.ReturnType.IsValueType)
1960
- {
1961
- il.Emit(OpCodes.Box, method.ReturnType);
1962
- }
1963
-
1964
- il.Emit(OpCodes.Ret);
1965
-
1966
- return (Func<object[], object>)
1967
- dynamicMethod.CreateDelegate(typeof(Func<object[], object>));
1968
- #endif
1349
+ return DelegateFactory.GetStaticMethodInvoker(method);
1969
1350
  }
1970
1351
 
1971
1352
  /// <summary>
@@ -1973,49 +1354,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
1973
1354
  /// </summary>
1974
1355
  public static Func<object[], object> GetConstructor(ConstructorInfo constructor)
1975
1356
  {
1976
- #if !EMIT_DYNAMIC_IL
1977
- return CreateCompiledConstructor(constructor);
1978
- #else
1979
- DynamicMethod dynamicMethod = new(
1980
- $"Create{constructor.DeclaringType.Name}",
1981
- typeof(object),
1982
- new[] { typeof(object[]) },
1983
- constructor.DeclaringType,
1984
- true
1985
- );
1986
-
1987
- ILGenerator il = dynamicMethod.GetILGenerator();
1988
- ParameterInfo[] parameters = constructor.GetParameters();
1989
-
1990
- for (int i = 0; i < parameters.Length; i++)
1991
- {
1992
- il.Emit(OpCodes.Ldarg_0);
1993
- il.Emit(OpCodes.Ldc_I4, i);
1994
- il.Emit(OpCodes.Ldelem_Ref);
1995
-
1996
- Type paramType = parameters[i].ParameterType;
1997
- if (paramType.IsValueType)
1998
- {
1999
- il.Emit(OpCodes.Unbox_Any, paramType);
2000
- }
2001
- else
2002
- {
2003
- il.Emit(OpCodes.Castclass, paramType);
2004
- }
2005
- }
2006
-
2007
- il.Emit(OpCodes.Newobj, constructor);
2008
-
2009
- if (constructor.DeclaringType.IsValueType)
2010
- {
2011
- il.Emit(OpCodes.Box, constructor.DeclaringType);
2012
- }
2013
-
2014
- il.Emit(OpCodes.Ret);
2015
-
2016
- return (Func<object[], object>)
2017
- dynamicMethod.CreateDelegate(typeof(Func<object[], object>));
2018
- #endif
1357
+ return DelegateFactory.GetConstructorInvoker(constructor);
2019
1358
  }
2020
1359
 
2021
1360
  /// <summary>
@@ -2050,20 +1389,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2050
1389
  throw new ArgumentException("Method signature does not match <T1,T2,TReturn>.");
2051
1390
  }
2052
1391
 
2053
- #if SINGLE_THREADED
2054
- if (!TypedStaticInvoker2.TryGetValue(method, out Delegate del))
2055
- {
2056
- del = BuildTypedStaticInvoker2<T1, T2, TReturn>(method);
2057
- TypedStaticInvoker2[method] = del;
2058
- }
2059
- return (Func<T1, T2, TReturn>)del;
2060
- #else
2061
- Delegate del = TypedStaticInvoker2.GetOrAdd(
2062
- method,
2063
- m => BuildTypedStaticInvoker2<T1, T2, TReturn>(m)
2064
- );
2065
- return (Func<T1, T2, TReturn>)del;
2066
- #endif
1392
+ return DelegateFactory.GetStaticMethodInvokerTyped<T1, T2, TReturn>(method);
2067
1393
  }
2068
1394
 
2069
1395
  public static Func<int, T[]> GetArrayCreator<T>()
@@ -2178,20 +1504,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2178
1504
  {
2179
1505
  throw new ArgumentException("Method signature does not match <TReturn>.");
2180
1506
  }
2181
- #if SINGLE_THREADED
2182
- if (!TypedStaticInvoker0.TryGetValue(method, out Delegate del))
2183
- {
2184
- del = BuildTypedStaticInvoker0<TReturn>(method);
2185
- TypedStaticInvoker0[method] = del;
2186
- }
2187
- return (Func<TReturn>)del;
2188
- #else
2189
- Delegate del = TypedStaticInvoker0.GetOrAdd(
2190
- method,
2191
- m => BuildTypedStaticInvoker0<TReturn>(m)
2192
- );
2193
- return (Func<TReturn>)del;
2194
- #endif
1507
+ return DelegateFactory.GetStaticMethodInvokerTyped<TReturn>(method);
2195
1508
  }
2196
1509
 
2197
1510
  public static Func<T1, TReturn> GetStaticMethodInvoker<T1, TReturn>(MethodInfo method)
@@ -2215,20 +1528,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2215
1528
  "ref/out parameters are not supported in typed invokers"
2216
1529
  );
2217
1530
  }
2218
- #if SINGLE_THREADED
2219
- if (!TypedStaticInvoker1.TryGetValue(method, out Delegate del))
2220
- {
2221
- del = BuildTypedStaticInvoker1<T1, TReturn>(method);
2222
- TypedStaticInvoker1[method] = del;
2223
- }
2224
- return (Func<T1, TReturn>)del;
2225
- #else
2226
- Delegate del = TypedStaticInvoker1.GetOrAdd(
2227
- method,
2228
- m => BuildTypedStaticInvoker1<T1, TReturn>(m)
2229
- );
2230
- return (Func<T1, TReturn>)del;
2231
- #endif
1531
+ return DelegateFactory.GetStaticMethodInvokerTyped<T1, TReturn>(method);
2232
1532
  }
2233
1533
 
2234
1534
  public static Func<T1, T2, T3, TReturn> GetStaticMethodInvoker<T1, T2, T3, TReturn>(
@@ -2259,20 +1559,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2259
1559
  "ref/out parameters are not supported in typed invokers"
2260
1560
  );
2261
1561
  }
2262
- #if SINGLE_THREADED
2263
- if (!TypedStaticInvoker3.TryGetValue(method, out Delegate del))
2264
- {
2265
- del = BuildTypedStaticInvoker3<T1, T2, T3, TReturn>(method);
2266
- TypedStaticInvoker3[method] = del;
2267
- }
2268
- return (Func<T1, T2, T3, TReturn>)del;
2269
- #else
2270
- Delegate del = TypedStaticInvoker3.GetOrAdd(
2271
- method,
2272
- m => BuildTypedStaticInvoker3<T1, T2, T3, TReturn>(m)
2273
- );
2274
- return (Func<T1, T2, T3, TReturn>)del;
2275
- #endif
1562
+ return DelegateFactory.GetStaticMethodInvokerTyped<T1, T2, T3, TReturn>(method);
2276
1563
  }
2277
1564
 
2278
1565
  public static Func<T1, T2, T3, T4, TReturn> GetStaticMethodInvoker<T1, T2, T3, T4, TReturn>(
@@ -2306,93 +1593,31 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2306
1593
  "ref/out parameters are not supported in typed invokers"
2307
1594
  );
2308
1595
  }
2309
- #if SINGLE_THREADED
2310
- if (!TypedStaticInvoker4.TryGetValue(method, out Delegate del))
2311
- {
2312
- del = BuildTypedStaticInvoker4<T1, T2, T3, T4, TReturn>(method);
2313
- TypedStaticInvoker4[method] = del;
2314
- }
2315
- return (Func<T1, T2, T3, T4, TReturn>)del;
2316
- #else
2317
- Delegate del = TypedStaticInvoker4.GetOrAdd(
2318
- method,
2319
- m => BuildTypedStaticInvoker4<T1, T2, T3, T4, TReturn>(m)
2320
- );
2321
- return (Func<T1, T2, T3, T4, TReturn>)del;
2322
- #endif
1596
+ return DelegateFactory.GetStaticMethodInvokerTyped<T1, T2, T3, T4, TReturn>(method);
2323
1597
  }
2324
1598
 
2325
1599
  public static Action GetStaticActionInvoker(MethodInfo method)
2326
1600
  {
2327
1601
  ValidateStaticActionSignature(method);
2328
- #if SINGLE_THREADED
2329
- if (!TypedStaticAction0.TryGetValue(method, out Delegate del))
2330
- {
2331
- del = BuildStaticActionInvoker0(method);
2332
- TypedStaticAction0[method] = del;
2333
- }
2334
- return (Action)del;
2335
- #else
2336
- Delegate del = TypedStaticAction0.GetOrAdd(method, m => BuildStaticActionInvoker0(m));
2337
- return (Action)del;
2338
- #endif
1602
+ return DelegateFactory.GetStaticActionInvokerTyped(method);
2339
1603
  }
2340
1604
 
2341
1605
  public static Action<T1> GetStaticActionInvoker<T1>(MethodInfo method)
2342
1606
  {
2343
1607
  ValidateStaticActionSignature(method, typeof(T1));
2344
- #if SINGLE_THREADED
2345
- if (!TypedStaticAction1.TryGetValue(method, out Delegate del))
2346
- {
2347
- del = BuildStaticActionInvoker1<T1>(method);
2348
- TypedStaticAction1[method] = del;
2349
- }
2350
- return (Action<T1>)del;
2351
- #else
2352
- Delegate del = TypedStaticAction1.GetOrAdd(
2353
- method,
2354
- m => BuildStaticActionInvoker1<T1>(m)
2355
- );
2356
- return (Action<T1>)del;
2357
- #endif
1608
+ return DelegateFactory.GetStaticActionInvokerTyped<T1>(method);
2358
1609
  }
2359
1610
 
2360
1611
  public static Action<T1, T2> GetStaticActionInvoker<T1, T2>(MethodInfo method)
2361
1612
  {
2362
1613
  ValidateStaticActionSignature(method, typeof(T1), typeof(T2));
2363
- #if SINGLE_THREADED
2364
- if (!TypedStaticAction2.TryGetValue(method, out Delegate del))
2365
- {
2366
- del = BuildStaticActionInvoker2<T1, T2>(method);
2367
- TypedStaticAction2[method] = del;
2368
- }
2369
- return (Action<T1, T2>)del;
2370
- #else
2371
- Delegate del = TypedStaticAction2.GetOrAdd(
2372
- method,
2373
- m => BuildStaticActionInvoker2<T1, T2>(m)
2374
- );
2375
- return (Action<T1, T2>)del;
2376
- #endif
1614
+ return DelegateFactory.GetStaticActionInvokerTyped<T1, T2>(method);
2377
1615
  }
2378
1616
 
2379
1617
  public static Action<T1, T2, T3> GetStaticActionInvoker<T1, T2, T3>(MethodInfo method)
2380
1618
  {
2381
1619
  ValidateStaticActionSignature(method, typeof(T1), typeof(T2), typeof(T3));
2382
- #if SINGLE_THREADED
2383
- if (!TypedStaticAction3.TryGetValue(method, out Delegate del))
2384
- {
2385
- del = BuildStaticActionInvoker3<T1, T2, T3>(method);
2386
- TypedStaticAction3[method] = del;
2387
- }
2388
- return (Action<T1, T2, T3>)del;
2389
- #else
2390
- Delegate del = TypedStaticAction3.GetOrAdd(
2391
- method,
2392
- m => BuildStaticActionInvoker3<T1, T2, T3>(m)
2393
- );
2394
- return (Action<T1, T2, T3>)del;
2395
- #endif
1620
+ return DelegateFactory.GetStaticActionInvokerTyped<T1, T2, T3>(method);
2396
1621
  }
2397
1622
 
2398
1623
  public static Action<T1, T2, T3, T4> GetStaticActionInvoker<T1, T2, T3, T4>(
@@ -2400,20 +1625,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2400
1625
  )
2401
1626
  {
2402
1627
  ValidateStaticActionSignature(method, typeof(T1), typeof(T2), typeof(T3), typeof(T4));
2403
- #if SINGLE_THREADED
2404
- if (!TypedStaticAction4.TryGetValue(method, out Delegate del))
2405
- {
2406
- del = BuildStaticActionInvoker4<T1, T2, T3, T4>(method);
2407
- TypedStaticAction4[method] = del;
2408
- }
2409
- return (Action<T1, T2, T3, T4>)del;
2410
- #else
2411
- Delegate del = TypedStaticAction4.GetOrAdd(
2412
- method,
2413
- m => BuildStaticActionInvoker4<T1, T2, T3, T4>(m)
2414
- );
2415
- return (Action<T1, T2, T3, T4>)del;
2416
- #endif
1628
+ return DelegateFactory.GetStaticActionInvokerTyped<T1, T2, T3, T4>(method);
2417
1629
  }
2418
1630
 
2419
1631
  public static Func<TInstance, TReturn> GetInstanceMethodInvoker<TInstance, TReturn>(
@@ -2421,20 +1633,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2421
1633
  )
2422
1634
  {
2423
1635
  ValidateInstanceFuncSignature<TInstance, TReturn>(method, Type.EmptyTypes);
2424
- #if SINGLE_THREADED
2425
- if (!TypedInstanceInvoker0.TryGetValue(method, out Delegate del))
2426
- {
2427
- del = BuildInstanceInvoker0<TInstance, TReturn>(method);
2428
- TypedInstanceInvoker0[method] = del;
2429
- }
2430
- return (Func<TInstance, TReturn>)del;
2431
- #else
2432
- Delegate del = TypedInstanceInvoker0.GetOrAdd(
2433
- method,
2434
- m => BuildInstanceInvoker0<TInstance, TReturn>(m)
2435
- );
2436
- return (Func<TInstance, TReturn>)del;
2437
- #endif
1636
+ return DelegateFactory.GetInstanceMethodInvokerTyped<TInstance, TReturn>(method);
2438
1637
  }
2439
1638
 
2440
1639
  public static Func<TInstance, T1, TReturn> GetInstanceMethodInvoker<TInstance, T1, TReturn>(
@@ -2442,20 +1641,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2442
1641
  )
2443
1642
  {
2444
1643
  ValidateInstanceFuncSignature<TInstance, TReturn>(method, new[] { typeof(T1) });
2445
- #if SINGLE_THREADED
2446
- if (!TypedInstanceInvoker1.TryGetValue(method, out Delegate del))
2447
- {
2448
- del = BuildInstanceInvoker1<TInstance, T1, TReturn>(method);
2449
- TypedInstanceInvoker1[method] = del;
2450
- }
2451
- return (Func<TInstance, T1, TReturn>)del;
2452
- #else
2453
- Delegate del = TypedInstanceInvoker1.GetOrAdd(
2454
- method,
2455
- m => BuildInstanceInvoker1<TInstance, T1, TReturn>(m)
2456
- );
2457
- return (Func<TInstance, T1, TReturn>)del;
2458
- #endif
1644
+ return DelegateFactory.GetInstanceMethodInvokerTyped<TInstance, T1, TReturn>(method);
2459
1645
  }
2460
1646
 
2461
1647
  public static Func<TInstance, T1, T2, TReturn> GetInstanceMethodInvoker<
@@ -2469,20 +1655,9 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2469
1655
  method,
2470
1656
  new[] { typeof(T1), typeof(T2) }
2471
1657
  );
2472
- #if SINGLE_THREADED
2473
- if (!TypedInstanceInvoker2.TryGetValue(method, out Delegate del))
2474
- {
2475
- del = BuildInstanceInvoker2<TInstance, T1, T2, TReturn>(method);
2476
- TypedInstanceInvoker2[method] = del;
2477
- }
2478
- return (Func<TInstance, T1, T2, TReturn>)del;
2479
- #else
2480
- Delegate del = TypedInstanceInvoker2.GetOrAdd(
2481
- method,
2482
- m => BuildInstanceInvoker2<TInstance, T1, T2, TReturn>(m)
1658
+ return DelegateFactory.GetInstanceMethodInvokerTyped<TInstance, T1, T2, TReturn>(
1659
+ method
2483
1660
  );
2484
- return (Func<TInstance, T1, T2, TReturn>)del;
2485
- #endif
2486
1661
  }
2487
1662
 
2488
1663
  public static Func<TInstance, T1, T2, T3, TReturn> GetInstanceMethodInvoker<
@@ -2497,20 +1672,9 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2497
1672
  method,
2498
1673
  new[] { typeof(T1), typeof(T2), typeof(T3) }
2499
1674
  );
2500
- #if SINGLE_THREADED
2501
- if (!TypedInstanceInvoker3.TryGetValue(method, out Delegate del))
2502
- {
2503
- del = BuildInstanceInvoker3<TInstance, T1, T2, T3, TReturn>(method);
2504
- TypedInstanceInvoker3[method] = del;
2505
- }
2506
- return (Func<TInstance, T1, T2, T3, TReturn>)del;
2507
- #else
2508
- Delegate del = TypedInstanceInvoker3.GetOrAdd(
2509
- method,
2510
- m => BuildInstanceInvoker3<TInstance, T1, T2, T3, TReturn>(m)
1675
+ return DelegateFactory.GetInstanceMethodInvokerTyped<TInstance, T1, T2, T3, TReturn>(
1676
+ method
2511
1677
  );
2512
- return (Func<TInstance, T1, T2, T3, TReturn>)del;
2513
- #endif
2514
1678
  }
2515
1679
 
2516
1680
  public static Func<TInstance, T1, T2, T3, T4, TReturn> GetInstanceMethodInvoker<
@@ -2526,39 +1690,20 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2526
1690
  method,
2527
1691
  new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }
2528
1692
  );
2529
- #if SINGLE_THREADED
2530
- if (!TypedInstanceInvoker4.TryGetValue(method, out Delegate del))
2531
- {
2532
- del = BuildInstanceInvoker4<TInstance, T1, T2, T3, T4, TReturn>(method);
2533
- TypedInstanceInvoker4[method] = del;
2534
- }
2535
- return (Func<TInstance, T1, T2, T3, T4, TReturn>)del;
2536
- #else
2537
- Delegate del = TypedInstanceInvoker4.GetOrAdd(
2538
- method,
2539
- m => BuildInstanceInvoker4<TInstance, T1, T2, T3, T4, TReturn>(m)
2540
- );
2541
- return (Func<TInstance, T1, T2, T3, T4, TReturn>)del;
2542
- #endif
1693
+ return DelegateFactory.GetInstanceMethodInvokerTyped<
1694
+ TInstance,
1695
+ T1,
1696
+ T2,
1697
+ T3,
1698
+ T4,
1699
+ TReturn
1700
+ >(method);
2543
1701
  }
2544
1702
 
2545
1703
  public static Action<TInstance> GetInstanceActionInvoker<TInstance>(MethodInfo method)
2546
1704
  {
2547
1705
  ValidateInstanceActionSignature<TInstance>(method, Type.EmptyTypes);
2548
- #if SINGLE_THREADED
2549
- if (!TypedInstanceAction0.TryGetValue(method, out Delegate del))
2550
- {
2551
- del = BuildInstanceActionInvoker0<TInstance>(method);
2552
- TypedInstanceAction0[method] = del;
2553
- }
2554
- return (Action<TInstance>)del;
2555
- #else
2556
- Delegate del = TypedInstanceAction0.GetOrAdd(
2557
- method,
2558
- m => BuildInstanceActionInvoker0<TInstance>(m)
2559
- );
2560
- return (Action<TInstance>)del;
2561
- #endif
1706
+ return DelegateFactory.GetInstanceActionInvokerTyped<TInstance>(method);
2562
1707
  }
2563
1708
 
2564
1709
  public static Action<TInstance, T1> GetInstanceActionInvoker<TInstance, T1>(
@@ -2566,20 +1711,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2566
1711
  )
2567
1712
  {
2568
1713
  ValidateInstanceActionSignature<TInstance>(method, new[] { typeof(T1) });
2569
- #if SINGLE_THREADED
2570
- if (!TypedInstanceAction1.TryGetValue(method, out Delegate del))
2571
- {
2572
- del = BuildInstanceActionInvoker1<TInstance, T1>(method);
2573
- TypedInstanceAction1[method] = del;
2574
- }
2575
- return (Action<TInstance, T1>)del;
2576
- #else
2577
- Delegate del = TypedInstanceAction1.GetOrAdd(
2578
- method,
2579
- m => BuildInstanceActionInvoker1<TInstance, T1>(m)
2580
- );
2581
- return (Action<TInstance, T1>)del;
2582
- #endif
1714
+ return DelegateFactory.GetInstanceActionInvokerTyped<TInstance, T1>(method);
2583
1715
  }
2584
1716
 
2585
1717
  public static Action<TInstance, T1, T2> GetInstanceActionInvoker<TInstance, T1, T2>(
@@ -2587,20 +1719,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2587
1719
  )
2588
1720
  {
2589
1721
  ValidateInstanceActionSignature<TInstance>(method, new[] { typeof(T1), typeof(T2) });
2590
- #if SINGLE_THREADED
2591
- if (!TypedInstanceAction2.TryGetValue(method, out Delegate del))
2592
- {
2593
- del = BuildInstanceActionInvoker2<TInstance, T1, T2>(method);
2594
- TypedInstanceAction2[method] = del;
2595
- }
2596
- return (Action<TInstance, T1, T2>)del;
2597
- #else
2598
- Delegate del = TypedInstanceAction2.GetOrAdd(
2599
- method,
2600
- m => BuildInstanceActionInvoker2<TInstance, T1, T2>(m)
2601
- );
2602
- return (Action<TInstance, T1, T2>)del;
2603
- #endif
1722
+ return DelegateFactory.GetInstanceActionInvokerTyped<TInstance, T1, T2>(method);
2604
1723
  }
2605
1724
 
2606
1725
  public static Action<TInstance, T1, T2, T3> GetInstanceActionInvoker<TInstance, T1, T2, T3>(
@@ -2611,20 +1730,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2611
1730
  method,
2612
1731
  new[] { typeof(T1), typeof(T2), typeof(T3) }
2613
1732
  );
2614
- #if SINGLE_THREADED
2615
- if (!TypedInstanceAction3.TryGetValue(method, out Delegate del))
2616
- {
2617
- del = BuildInstanceActionInvoker3<TInstance, T1, T2, T3>(method);
2618
- TypedInstanceAction3[method] = del;
2619
- }
2620
- return (Action<TInstance, T1, T2, T3>)del;
2621
- #else
2622
- Delegate del = TypedInstanceAction3.GetOrAdd(
2623
- method,
2624
- m => BuildInstanceActionInvoker3<TInstance, T1, T2, T3>(m)
2625
- );
2626
- return (Action<TInstance, T1, T2, T3>)del;
2627
- #endif
1733
+ return DelegateFactory.GetInstanceActionInvokerTyped<TInstance, T1, T2, T3>(method);
2628
1734
  }
2629
1735
 
2630
1736
  public static Action<TInstance, T1, T2, T3, T4> GetInstanceActionInvoker<
@@ -2639,108 +1745,164 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
2639
1745
  method,
2640
1746
  new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }
2641
1747
  );
2642
- #if SINGLE_THREADED
2643
- if (!TypedInstanceAction4.TryGetValue(method, out Delegate del))
2644
- {
2645
- del = BuildInstanceActionInvoker4<TInstance, T1, T2, T3, T4>(method);
2646
- TypedInstanceAction4[method] = del;
2647
- }
2648
- return (Action<TInstance, T1, T2, T3, T4>)del;
2649
- #else
2650
- Delegate del = TypedInstanceAction4.GetOrAdd(
2651
- method,
2652
- m => BuildInstanceActionInvoker4<TInstance, T1, T2, T3, T4>(m)
2653
- );
2654
- return (Action<TInstance, T1, T2, T3, T4>)del;
2655
- #endif
1748
+ return DelegateFactory.GetInstanceActionInvokerTyped<TInstance, T1, T2, T3, T4>(method);
2656
1749
  }
2657
1750
 
2658
1751
  private static Delegate BuildTypedStaticInvoker2<T1, T2, TReturn>(MethodInfo method)
2659
1752
  {
2660
- #if !EMIT_DYNAMIC_IL
1753
+ if (ExpressionsEnabled)
1754
+ {
1755
+ try
1756
+ {
1757
+ ParameterExpression p1 = Expression.Parameter(typeof(T1), "a");
1758
+ ParameterExpression p2 = Expression.Parameter(typeof(T2), "b");
1759
+ MethodCallExpression call = Expression.Call(method, p1, p2);
1760
+ return Expression.Lambda<Func<T1, T2, TReturn>>(call, p1, p2).Compile();
1761
+ }
1762
+ catch
1763
+ {
1764
+ // continue to alternative strategies
1765
+ }
1766
+ }
1767
+
1768
+ #if EMIT_DYNAMIC_IL
1769
+ if (DynamicIlEnabled)
1770
+ {
1771
+ try
1772
+ {
1773
+ DynamicMethod dm = new(
1774
+ $"InvokeStatic2_{method.DeclaringType?.Name}_{method.Name}",
1775
+ typeof(TReturn),
1776
+ new[] { typeof(T1), typeof(T2) },
1777
+ method.Module,
1778
+ true
1779
+ );
1780
+ ILGenerator il = dm.GetILGenerator();
1781
+ il.Emit(OpCodes.Ldarg_0);
1782
+ il.Emit(OpCodes.Ldarg_1);
1783
+ il.Emit(OpCodes.Call, method);
1784
+ il.Emit(OpCodes.Ret);
1785
+ return dm.CreateDelegate(typeof(Func<T1, T2, TReturn>));
1786
+ }
1787
+ catch
1788
+ {
1789
+ // ignore and fall back to reflection
1790
+ }
1791
+ }
1792
+ #endif
1793
+
2661
1794
  try
2662
1795
  {
2663
1796
  return method.CreateDelegate(typeof(Func<T1, T2, TReturn>));
2664
1797
  }
2665
1798
  catch
2666
1799
  {
2667
- // Fallback to expression compilation
2668
- ParameterExpression p1 = Expression.Parameter(typeof(T1), "a");
2669
- ParameterExpression p2 = Expression.Parameter(typeof(T2), "b");
2670
- MethodCallExpression call = Expression.Call(method, p1, p2);
2671
- return Expression.Lambda<Func<T1, T2, TReturn>>(call, p1, p2).Compile();
1800
+ return new Func<T1, T2, TReturn>(
1801
+ (a, b) => (TReturn)method.Invoke(null, new object[] { a, b })
1802
+ );
2672
1803
  }
2673
- #else
2674
- DynamicMethod dm = new(
2675
- $"InvokeStatic2_{method.DeclaringType?.Name}_{method.Name}",
2676
- typeof(TReturn),
2677
- new[] { typeof(T1), typeof(T2) },
2678
- method.Module,
2679
- true
2680
- );
2681
- ILGenerator il = dm.GetILGenerator();
2682
- il.Emit(OpCodes.Ldarg_0);
2683
- il.Emit(OpCodes.Ldarg_1);
2684
- il.Emit(OpCodes.Call, method);
2685
- il.Emit(OpCodes.Ret);
2686
- return dm.CreateDelegate(typeof(Func<T1, T2, TReturn>));
2687
- #endif
2688
1804
  }
2689
1805
 
2690
1806
  private static Delegate BuildTypedStaticInvoker0<TReturn>(MethodInfo method)
2691
1807
  {
2692
- #if !EMIT_DYNAMIC_IL
1808
+ if (ExpressionsEnabled)
1809
+ {
1810
+ try
1811
+ {
1812
+ MethodCallExpression call = Expression.Call(method);
1813
+ return Expression.Lambda<Func<TReturn>>(call).Compile();
1814
+ }
1815
+ catch
1816
+ {
1817
+ // Fall through to alternative strategies
1818
+ }
1819
+ }
1820
+
1821
+ #if EMIT_DYNAMIC_IL
1822
+ if (DynamicIlEnabled)
1823
+ {
1824
+ try
1825
+ {
1826
+ DynamicMethod dm = new(
1827
+ $"InvokeStatic0_{method.DeclaringType?.Name}_{method.Name}",
1828
+ typeof(TReturn),
1829
+ Type.EmptyTypes,
1830
+ method.Module,
1831
+ true
1832
+ );
1833
+ ILGenerator il = dm.GetILGenerator();
1834
+ il.Emit(OpCodes.Call, method);
1835
+ il.Emit(OpCodes.Ret);
1836
+ return dm.CreateDelegate(typeof(Func<TReturn>));
1837
+ }
1838
+ catch
1839
+ {
1840
+ // Ignore and fall back to reflection
1841
+ }
1842
+ }
1843
+ #endif
1844
+
2693
1845
  try
2694
1846
  {
2695
1847
  return method.CreateDelegate(typeof(Func<TReturn>));
2696
1848
  }
2697
1849
  catch
2698
1850
  {
2699
- MethodCallExpression call = Expression.Call(method);
2700
- return Expression.Lambda<Func<TReturn>>(call).Compile();
1851
+ return new Func<TReturn>(() => (TReturn)method.Invoke(null, Array.Empty<object>()));
2701
1852
  }
2702
- #else
2703
- DynamicMethod dm = new(
2704
- $"InvokeStatic0_{method.DeclaringType?.Name}_{method.Name}",
2705
- typeof(TReturn),
2706
- Type.EmptyTypes,
2707
- method.Module,
2708
- true
2709
- );
2710
- ILGenerator il = dm.GetILGenerator();
2711
- il.Emit(OpCodes.Call, method);
2712
- il.Emit(OpCodes.Ret);
2713
- return dm.CreateDelegate(typeof(Func<TReturn>));
2714
- #endif
2715
1853
  }
2716
1854
 
2717
1855
  private static Delegate BuildTypedStaticInvoker1<T1, TReturn>(MethodInfo method)
2718
1856
  {
2719
- #if !EMIT_DYNAMIC_IL
1857
+ if (ExpressionsEnabled)
1858
+ {
1859
+ try
1860
+ {
1861
+ ParameterExpression a = Expression.Parameter(typeof(T1), "a");
1862
+ MethodCallExpression call = Expression.Call(method, a);
1863
+ return Expression.Lambda<Func<T1, TReturn>>(call, a).Compile();
1864
+ }
1865
+ catch
1866
+ {
1867
+ // Continue to alternative strategies
1868
+ }
1869
+ }
1870
+
1871
+ #if EMIT_DYNAMIC_IL
1872
+ if (DynamicIlEnabled)
1873
+ {
1874
+ try
1875
+ {
1876
+ DynamicMethod dm = new(
1877
+ $"InvokeStatic1_{method.DeclaringType?.Name}_{method.Name}",
1878
+ typeof(TReturn),
1879
+ new[] { typeof(T1) },
1880
+ method.Module,
1881
+ true
1882
+ );
1883
+ ILGenerator il = dm.GetILGenerator();
1884
+ il.Emit(OpCodes.Ldarg_0);
1885
+ il.Emit(OpCodes.Call, method);
1886
+ il.Emit(OpCodes.Ret);
1887
+ return dm.CreateDelegate(typeof(Func<T1, TReturn>));
1888
+ }
1889
+ catch
1890
+ {
1891
+ // fall through
1892
+ }
1893
+ }
1894
+ #endif
1895
+
2720
1896
  try
2721
1897
  {
2722
1898
  return method.CreateDelegate(typeof(Func<T1, TReturn>));
2723
1899
  }
2724
1900
  catch
2725
1901
  {
2726
- ParameterExpression a = Expression.Parameter(typeof(T1), "a");
2727
- MethodCallExpression call = Expression.Call(method, a);
2728
- return Expression.Lambda<Func<T1, TReturn>>(call, a).Compile();
1902
+ return new Func<T1, TReturn>(arg =>
1903
+ (TReturn)method.Invoke(null, new object[] { arg })
1904
+ );
2729
1905
  }
2730
- #else
2731
- DynamicMethod dm = new(
2732
- $"InvokeStatic1_{method.DeclaringType?.Name}_{method.Name}",
2733
- typeof(TReturn),
2734
- new[] { typeof(T1) },
2735
- method.Module,
2736
- true
2737
- );
2738
- ILGenerator il = dm.GetILGenerator();
2739
- il.Emit(OpCodes.Ldarg_0);
2740
- il.Emit(OpCodes.Call, method);
2741
- il.Emit(OpCodes.Ret);
2742
- return dm.CreateDelegate(typeof(Func<T1, TReturn>));
2743
- #endif
2744
1906
  }
2745
1907
 
2746
1908
  private static Delegate BuildTypedStaticInvoker3<T1, T2, T3, TReturn>(MethodInfo method)
@@ -3557,24 +2719,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
3557
2719
  $"Type {type.Name} does not have a parameterless constructor"
3558
2720
  );
3559
2721
  }
3560
-
3561
- #if !EMIT_DYNAMIC_IL
3562
- return CreateCompiledParameterlessConstructor<T>(constructor);
3563
- #else
3564
- DynamicMethod dynamicMethod = new(
3565
- $"CreateParameterless{type.Name}",
3566
- typeof(T),
3567
- Type.EmptyTypes,
3568
- type,
3569
- true
3570
- );
3571
-
3572
- ILGenerator il = dynamicMethod.GetILGenerator();
3573
- il.Emit(OpCodes.Newobj, constructor);
3574
- il.Emit(OpCodes.Ret);
3575
-
3576
- return (Func<T>)dynamicMethod.CreateDelegate(typeof(Func<T>));
3577
- #endif
2722
+ return DelegateFactory.GetParameterlessConstructorTyped<T>(constructor);
3578
2723
  }
3579
2724
 
3580
2725
  /// <summary>
@@ -3593,28 +2738,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
3593
2738
  $"Type {constructedType.Name} does not have a parameterless constructor"
3594
2739
  );
3595
2740
  }
3596
-
3597
- #if !EMIT_DYNAMIC_IL
3598
- return CreateCompiledGenericParameterlessConstructor<T>(constructedType, constructor);
3599
- #else
3600
- DynamicMethod dynamicMethod = new(
3601
- $"CreateGenericParameterless{constructedType.Name}",
3602
- typeof(T),
3603
- Type.EmptyTypes,
3604
- constructedType,
3605
- true
3606
- );
3607
-
3608
- ILGenerator il = dynamicMethod.GetILGenerator();
3609
- il.Emit(OpCodes.Newobj, constructor);
3610
- if (constructedType.IsValueType && typeof(T) == typeof(object))
3611
- {
3612
- il.Emit(OpCodes.Box, constructedType);
3613
- }
3614
- il.Emit(OpCodes.Ret);
3615
-
3616
- return (Func<T>)dynamicMethod.CreateDelegate(typeof(Func<T>));
3617
- #endif
2741
+ return DelegateFactory.GetParameterlessConstructorTyped<T>(constructor);
3618
2742
  }
3619
2743
 
3620
2744
  /// <summary>
@@ -4010,7 +3134,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4010
3134
  FieldLookup[key] = field;
4011
3135
  }
4012
3136
  #else
4013
- field = FieldLookup.GetOrAdd(key, k => k.type.GetField(k.name, k.flags));
3137
+ field = FieldLookup.GetOrAdd(key, static k => k.type.GetField(k.name, k.flags));
4014
3138
  #endif
4015
3139
  return field != null;
4016
3140
  }
@@ -4040,7 +3164,10 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4040
3164
  PropertyLookup[key] = property;
4041
3165
  }
4042
3166
  #else
4043
- property = PropertyLookup.GetOrAdd(key, k => k.type.GetProperty(k.name, k.flags));
3167
+ property = PropertyLookup.GetOrAdd(
3168
+ key,
3169
+ static k => k.type.GetProperty(k.name, k.flags)
3170
+ );
4044
3171
  #endif
4045
3172
  return property != null;
4046
3173
  }
@@ -4083,20 +3210,21 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4083
3210
  #else
4084
3211
  method = MethodLookup.GetOrAdd(
4085
3212
  key,
4086
- k =>
3213
+ static (tuple, state) =>
4087
3214
  {
4088
- if (paramTypes == null)
3215
+ if (state.parameterTypes == null)
4089
3216
  {
4090
- return k.type.GetMethod(name, k.flags);
3217
+ return tuple.type.GetMethod(state.methodName, tuple.flags);
4091
3218
  }
4092
- return k.type.GetMethod(
4093
- name,
4094
- k.flags,
3219
+ return tuple.type.GetMethod(
3220
+ state.methodName,
3221
+ tuple.flags,
4095
3222
  binder: null,
4096
- types: paramTypes,
3223
+ types: state.parameterTypes,
4097
3224
  modifiers: null
4098
3225
  );
4099
- }
3226
+ },
3227
+ (methodName: name, parameterTypes: paramTypes)
4100
3228
  );
4101
3229
  #endif
4102
3230
  return method != null;
@@ -4165,11 +3293,32 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4165
3293
 
4166
3294
  #if SINGLE_THREADED
4167
3295
  private static readonly Dictionary<Type, Func<object, bool>> EnabledPropertyGetters = new();
3296
+
3297
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
3298
+ private static Func<object, bool> GetEnabledPropertyGetterCached(Type componentType)
3299
+ {
3300
+ if (!EnabledPropertyGetters.TryGetValue(componentType, out Func<object, bool> getter))
3301
+ {
3302
+ getter = BuildEnabledPropertyGetter(componentType);
3303
+ EnabledPropertyGetters[componentType] = getter;
3304
+ }
3305
+
3306
+ return getter;
3307
+ }
4168
3308
  #else
4169
3309
  private static readonly ConcurrentDictionary<
4170
3310
  Type,
4171
3311
  Func<object, bool>
4172
3312
  > EnabledPropertyGetters = new();
3313
+
3314
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
3315
+ private static Func<object, bool> GetEnabledPropertyGetterCached(Type componentType)
3316
+ {
3317
+ return EnabledPropertyGetters.GetOrAdd(
3318
+ componentType,
3319
+ static type => BuildEnabledPropertyGetter(type)
3320
+ );
3321
+ }
4173
3322
  #endif
4174
3323
 
4175
3324
  private static Func<object, bool> BuildEnabledPropertyGetter(Type type)
@@ -4228,7 +3377,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4228
3377
  Type type
4229
3378
  )
4230
3379
  {
4231
- if (!CanCompileExpressions)
3380
+ if (!ExpressionsEnabled)
4232
3381
  {
4233
3382
  return CreateDelegateEnabledPropertyGetter(property, type)
4234
3383
  ?? (instance => (bool)property.GetValue(instance));
@@ -4302,10 +3451,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4302
3451
  }
4303
3452
 
4304
3453
  Type componentType = component.GetType();
4305
- Func<object, bool> enabledGetter = EnabledPropertyGetters.GetOrAdd(
4306
- componentType,
4307
- inputType => BuildEnabledPropertyGetter(inputType)
4308
- );
3454
+ Func<object, bool> enabledGetter = GetEnabledPropertyGetterCached(componentType);
4309
3455
 
4310
3456
  if (enabledGetter == null)
4311
3457
  {
@@ -4606,9 +3752,202 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4606
3752
  }
4607
3753
  }
4608
3754
 
4609
- private static bool CheckExpressionCompilationSupport()
3755
+ private static bool CheckDynamicIlSupport()
4610
3756
  {
4611
- #if !SUPPORT_EXPRESSION_COMPILE
3757
+ #if !EMIT_DYNAMIC_IL
3758
+ return false;
3759
+ #else
3760
+ try
3761
+ {
3762
+ DynamicMethod dynamicMethod = new(
3763
+ "UnityHelpersIlProbe",
3764
+ typeof(int),
3765
+ Type.EmptyTypes,
3766
+ typeof(ReflectionHelpers),
3767
+ true
3768
+ );
3769
+ ILGenerator il = dynamicMethod.GetILGenerator();
3770
+ il.Emit(OpCodes.Ldc_I4_0);
3771
+ il.Emit(OpCodes.Ret);
3772
+ _ = ((Func<int>)dynamicMethod.CreateDelegate(typeof(Func<int>)))();
3773
+ return true;
3774
+ }
3775
+ catch
3776
+ {
3777
+ return false;
3778
+ }
3779
+ #endif
3780
+ }
3781
+
3782
+ #if EMIT_DYNAMIC_IL
3783
+ private static Func<object, object[], object> BuildMethodInvokerIL(MethodInfo method)
3784
+ {
3785
+ try
3786
+ {
3787
+ DynamicMethod dynamicMethod = new(
3788
+ $"Invoke{method.DeclaringType.Name}_{method.Name}",
3789
+ typeof(object),
3790
+ new[] { typeof(object), typeof(object[]) },
3791
+ method.DeclaringType,
3792
+ true
3793
+ );
3794
+
3795
+ ILGenerator il = dynamicMethod.GetILGenerator();
3796
+ ParameterInfo[] parameters = method.GetParameters();
3797
+
3798
+ il.Emit(OpCodes.Ldarg_0);
3799
+ il.Emit(
3800
+ method.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass,
3801
+ method.DeclaringType
3802
+ );
3803
+
3804
+ for (int i = 0; i < parameters.Length; i++)
3805
+ {
3806
+ il.Emit(OpCodes.Ldarg_1);
3807
+ il.Emit(OpCodes.Ldc_I4, i);
3808
+ il.Emit(OpCodes.Ldelem_Ref);
3809
+
3810
+ Type paramType = parameters[i].ParameterType;
3811
+ if (paramType.IsValueType)
3812
+ {
3813
+ il.Emit(OpCodes.Unbox_Any, paramType);
3814
+ }
3815
+ else
3816
+ {
3817
+ il.Emit(OpCodes.Castclass, paramType);
3818
+ }
3819
+ }
3820
+
3821
+ il.Emit(method.DeclaringType.IsValueType ? OpCodes.Call : OpCodes.Callvirt, method);
3822
+
3823
+ if (method.ReturnType == typeof(void))
3824
+ {
3825
+ il.Emit(OpCodes.Ldnull);
3826
+ }
3827
+ else if (method.ReturnType.IsValueType)
3828
+ {
3829
+ il.Emit(OpCodes.Box, method.ReturnType);
3830
+ }
3831
+
3832
+ il.Emit(OpCodes.Ret);
3833
+
3834
+ return (Func<object, object[], object>)
3835
+ dynamicMethod.CreateDelegate(typeof(Func<object, object[], object>));
3836
+ }
3837
+ catch
3838
+ {
3839
+ return null;
3840
+ }
3841
+ }
3842
+
3843
+ private static Func<object[], object> BuildStaticMethodInvokerIL(MethodInfo method)
3844
+ {
3845
+ try
3846
+ {
3847
+ DynamicMethod dynamicMethod = new(
3848
+ $"InvokeStatic{method.DeclaringType.Name}_{method.Name}",
3849
+ typeof(object),
3850
+ new[] { typeof(object[]) },
3851
+ method.DeclaringType,
3852
+ true
3853
+ );
3854
+
3855
+ ILGenerator il = dynamicMethod.GetILGenerator();
3856
+ ParameterInfo[] parameters = method.GetParameters();
3857
+
3858
+ for (int i = 0; i < parameters.Length; i++)
3859
+ {
3860
+ il.Emit(OpCodes.Ldarg_0);
3861
+ il.Emit(OpCodes.Ldc_I4, i);
3862
+ il.Emit(OpCodes.Ldelem_Ref);
3863
+
3864
+ Type paramType = parameters[i].ParameterType;
3865
+ if (paramType.IsValueType)
3866
+ {
3867
+ il.Emit(OpCodes.Unbox_Any, paramType);
3868
+ }
3869
+ else
3870
+ {
3871
+ il.Emit(OpCodes.Castclass, paramType);
3872
+ }
3873
+ }
3874
+
3875
+ il.Emit(OpCodes.Call, method);
3876
+
3877
+ if (method.ReturnType == typeof(void))
3878
+ {
3879
+ il.Emit(OpCodes.Ldnull);
3880
+ }
3881
+ else if (method.ReturnType.IsValueType)
3882
+ {
3883
+ il.Emit(OpCodes.Box, method.ReturnType);
3884
+ }
3885
+
3886
+ il.Emit(OpCodes.Ret);
3887
+
3888
+ return (Func<object[], object>)
3889
+ dynamicMethod.CreateDelegate(typeof(Func<object[], object>));
3890
+ }
3891
+ catch
3892
+ {
3893
+ return null;
3894
+ }
3895
+ }
3896
+
3897
+ private static Func<object[], object> BuildConstructorIL(ConstructorInfo constructor)
3898
+ {
3899
+ try
3900
+ {
3901
+ DynamicMethod dynamicMethod = new(
3902
+ $"Create{constructor.DeclaringType.Name}",
3903
+ typeof(object),
3904
+ new[] { typeof(object[]) },
3905
+ constructor.DeclaringType,
3906
+ true
3907
+ );
3908
+
3909
+ ILGenerator il = dynamicMethod.GetILGenerator();
3910
+ ParameterInfo[] parameters = constructor.GetParameters();
3911
+
3912
+ for (int i = 0; i < parameters.Length; i++)
3913
+ {
3914
+ il.Emit(OpCodes.Ldarg_0);
3915
+ il.Emit(OpCodes.Ldc_I4, i);
3916
+ il.Emit(OpCodes.Ldelem_Ref);
3917
+
3918
+ Type paramType = parameters[i].ParameterType;
3919
+ if (paramType.IsValueType)
3920
+ {
3921
+ il.Emit(OpCodes.Unbox_Any, paramType);
3922
+ }
3923
+ else
3924
+ {
3925
+ il.Emit(OpCodes.Castclass, paramType);
3926
+ }
3927
+ }
3928
+
3929
+ il.Emit(OpCodes.Newobj, constructor);
3930
+
3931
+ if (constructor.DeclaringType.IsValueType)
3932
+ {
3933
+ il.Emit(OpCodes.Box, constructor.DeclaringType);
3934
+ }
3935
+
3936
+ il.Emit(OpCodes.Ret);
3937
+
3938
+ return (Func<object[], object>)
3939
+ dynamicMethod.CreateDelegate(typeof(Func<object[], object>));
3940
+ }
3941
+ catch
3942
+ {
3943
+ return null;
3944
+ }
3945
+ }
3946
+ #endif
3947
+
3948
+ private static bool CheckExpressionCompilationSupport()
3949
+ {
3950
+ #if !SUPPORT_EXPRESSION_COMPILE
4612
3951
  return false;
4613
3952
  #else
4614
3953
  try
@@ -4631,7 +3970,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4631
3970
  MethodInfo addMethod
4632
3971
  )
4633
3972
  {
4634
- if (!CanCompileExpressions)
3973
+ if (!ExpressionsEnabled)
4635
3974
  {
4636
3975
  return null;
4637
3976
  }
@@ -4642,9 +3981,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4642
3981
  ParameterExpression valueParam = Expression.Parameter(typeof(object), "value");
4643
3982
 
4644
3983
  Expression castSet = Expression.Convert(setParam, hashSetType);
4645
- Expression castValue = elementType.IsValueType
4646
- ? Expression.Convert(valueParam, elementType)
4647
- : Expression.Convert(valueParam, elementType);
3984
+ Expression castValue = Expression.Convert(valueParam, elementType);
4648
3985
 
4649
3986
  MethodCallExpression call = Expression.Call(castSet, addMethod, castValue);
4650
3987
  Expression body =
@@ -4664,7 +4001,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4664
4001
 
4665
4002
  private static Func<object, object[], object> CreateCompiledMethodInvoker(MethodInfo method)
4666
4003
  {
4667
- if (!CanCompileExpressions)
4004
+ if (!ExpressionsEnabled)
4668
4005
  {
4669
4006
  return CreateDelegateMethodInvoker(method)
4670
4007
  ?? ((instance, args) => method.Invoke(instance, args));
@@ -4726,7 +4063,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4726
4063
 
4727
4064
  private static Func<object[], object> CreateCompiledStaticMethodInvoker(MethodInfo method)
4728
4065
  {
4729
- if (!CanCompileExpressions)
4066
+ if (!ExpressionsEnabled)
4730
4067
  {
4731
4068
  return CreateDelegateStaticMethodInvoker(method)
4732
4069
  ?? (args => method.Invoke(null, args));
@@ -4772,7 +4109,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4772
4109
 
4773
4110
  private static Func<object[], object> CreateCompiledConstructor(ConstructorInfo constructor)
4774
4111
  {
4775
- if (!CanCompileExpressions)
4112
+ if (!ExpressionsEnabled)
4776
4113
  {
4777
4114
  return CreateDelegateConstructor(constructor) ?? (args => constructor.Invoke(args));
4778
4115
  }
@@ -4802,140 +4139,1185 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4802
4139
  ? Expression.Convert(newExpression, typeof(object))
4803
4140
  : newExpression;
4804
4141
 
4805
- return Expression
4806
- .Lambda<Func<object[], object>>(returnExpression, argsParam)
4807
- .Compile();
4808
- }
4809
- catch
4810
- {
4811
- return args => constructor.Invoke(args);
4142
+ return Expression
4143
+ .Lambda<Func<object[], object>>(returnExpression, argsParam)
4144
+ .Compile();
4145
+ }
4146
+ catch
4147
+ {
4148
+ return args => constructor.Invoke(args);
4149
+ }
4150
+ }
4151
+
4152
+ private static Func<object> CreateCompiledParameterlessConstructor(
4153
+ ConstructorInfo constructor
4154
+ )
4155
+ {
4156
+ if (!ExpressionsEnabled)
4157
+ {
4158
+ return CreateDelegateParameterlessConstructor(constructor)
4159
+ ?? (() => constructor.Invoke(null));
4160
+ }
4161
+
4162
+ try
4163
+ {
4164
+ Expression newExpression = Expression.New(constructor);
4165
+ Expression body = constructor.DeclaringType.IsValueType
4166
+ ? Expression.Convert(newExpression, typeof(object))
4167
+ : newExpression;
4168
+
4169
+ return Expression.Lambda<Func<object>>(body).Compile();
4170
+ }
4171
+ catch
4172
+ {
4173
+ return CreateDelegateParameterlessConstructor(constructor)
4174
+ ?? (() => constructor.Invoke(null));
4175
+ }
4176
+ }
4177
+
4178
+ private static Func<T> CreateCompiledParameterlessConstructor<T>(
4179
+ ConstructorInfo constructor,
4180
+ Type constructedType
4181
+ )
4182
+ {
4183
+ if (!ExpressionsEnabled)
4184
+ {
4185
+ if (constructedType == typeof(T))
4186
+ {
4187
+ return CreateDelegateParameterlessConstructor<T>(constructor)
4188
+ ?? (() => (T)constructor.Invoke(null));
4189
+ }
4190
+
4191
+ return () => (T)constructor.Invoke(null);
4192
+ }
4193
+
4194
+ try
4195
+ {
4196
+ Expression newExpression = Expression.New(constructor);
4197
+ Expression body =
4198
+ constructedType.IsValueType && typeof(T) != constructedType
4199
+ ? Expression.Convert(newExpression, typeof(T))
4200
+ : constructedType == typeof(T) ? newExpression
4201
+ : Expression.Convert(newExpression, typeof(T));
4202
+
4203
+ return Expression.Lambda<Func<T>>(body).Compile();
4204
+ }
4205
+ catch
4206
+ {
4207
+ return () => (T)constructor.Invoke(null);
4208
+ }
4209
+ }
4210
+
4211
+ private static Func<object, object[], object> CreateCompiledIndexerGetter(
4212
+ PropertyInfo property
4213
+ )
4214
+ {
4215
+ if (!ExpressionsEnabled)
4216
+ {
4217
+ return null;
4218
+ }
4219
+
4220
+ try
4221
+ {
4222
+ ParameterExpression instanceParam = Expression.Parameter(
4223
+ typeof(object),
4224
+ "instance"
4225
+ );
4226
+ ParameterExpression argsParam = Expression.Parameter(typeof(object[]), "args");
4227
+
4228
+ MethodInfo getMethod = property.GetGetMethod(true);
4229
+ if (getMethod == null)
4230
+ {
4231
+ return null;
4232
+ }
4233
+
4234
+ Expression targetExpression = property.DeclaringType.IsValueType
4235
+ ? Expression.Unbox(instanceParam, property.DeclaringType)
4236
+ : Expression.Convert(instanceParam, property.DeclaringType);
4237
+
4238
+ ParameterInfo[] indices = property.GetIndexParameters();
4239
+ Expression[] indexExpressions = new Expression[indices.Length];
4240
+ for (int i = 0; i < indices.Length; i++)
4241
+ {
4242
+ Expression element = Expression.ArrayIndex(argsParam, Expression.Constant(i));
4243
+ Type indexType = indices[i].ParameterType;
4244
+ indexExpressions[i] = indexType.IsValueType
4245
+ ? Expression.Unbox(element, indexType)
4246
+ : Expression.Convert(element, indexType);
4247
+ }
4248
+
4249
+ Expression propertyExpression = getMethod.IsStatic
4250
+ ? Expression.Property(null, property, indexExpressions)
4251
+ : Expression.Property(targetExpression, property, indexExpressions);
4252
+
4253
+ Expression body = property.PropertyType.IsValueType
4254
+ ? Expression.Convert(propertyExpression, typeof(object))
4255
+ : propertyExpression;
4256
+
4257
+ return Expression
4258
+ .Lambda<Func<object, object[], object>>(body, instanceParam, argsParam)
4259
+ .Compile();
4260
+ }
4261
+ catch
4262
+ {
4263
+ return null;
4264
+ }
4265
+ }
4266
+
4267
+ private static Action<object, object, object[]> CreateCompiledIndexerSetter(
4268
+ PropertyInfo property
4269
+ )
4270
+ {
4271
+ if (!ExpressionsEnabled)
4272
+ {
4273
+ return null;
4274
+ }
4275
+
4276
+ try
4277
+ {
4278
+ MethodInfo setMethod = property.GetSetMethod(true);
4279
+ if (setMethod == null)
4280
+ {
4281
+ return null;
4282
+ }
4283
+
4284
+ ParameterExpression instanceParam = Expression.Parameter(
4285
+ typeof(object),
4286
+ "instance"
4287
+ );
4288
+ ParameterExpression valueParam = Expression.Parameter(typeof(object), "value");
4289
+ ParameterExpression argsParam = Expression.Parameter(typeof(object[]), "args");
4290
+
4291
+ Expression targetExpression = property.DeclaringType.IsValueType
4292
+ ? Expression.Unbox(instanceParam, property.DeclaringType)
4293
+ : Expression.Convert(instanceParam, property.DeclaringType);
4294
+
4295
+ ParameterInfo[] indices = property.GetIndexParameters();
4296
+ Expression[] indexExpressions = new Expression[indices.Length];
4297
+ for (int i = 0; i < indices.Length; i++)
4298
+ {
4299
+ Expression element = Expression.ArrayIndex(argsParam, Expression.Constant(i));
4300
+ Type indexType = indices[i].ParameterType;
4301
+ indexExpressions[i] = indexType.IsValueType
4302
+ ? Expression.Unbox(element, indexType)
4303
+ : Expression.Convert(element, indexType);
4304
+ }
4305
+
4306
+ Expression valueExpression = property.PropertyType.IsValueType
4307
+ ? Expression.Unbox(valueParam, property.PropertyType)
4308
+ : Expression.Convert(valueParam, property.PropertyType);
4309
+
4310
+ IEnumerable<Expression> arguments = indexExpressions.Concat(
4311
+ new[] { valueExpression }
4312
+ );
4313
+ Expression callExpression = setMethod.IsStatic
4314
+ ? Expression.Call(setMethod, arguments)
4315
+ : Expression.Call(targetExpression, setMethod, arguments);
4316
+
4317
+ return Expression
4318
+ .Lambda<Action<object, object, object[]>>(
4319
+ callExpression,
4320
+ instanceParam,
4321
+ valueParam,
4322
+ argsParam
4323
+ )
4324
+ .Compile();
4325
+ }
4326
+ catch
4327
+ {
4328
+ return null;
4329
+ }
4330
+ }
4331
+
4332
+ private static Func<T> CreateCompiledParameterlessConstructor<T>(
4333
+ ConstructorInfo constructor
4334
+ )
4335
+ {
4336
+ return CreateCompiledParameterlessConstructor<T>(
4337
+ constructor,
4338
+ constructor.DeclaringType
4339
+ );
4340
+ }
4341
+
4342
+ private static Func<object, object> CreateCompiledFieldGetter(FieldInfo field)
4343
+ {
4344
+ try
4345
+ {
4346
+ ParameterExpression instanceParam = Expression.Parameter(
4347
+ typeof(object),
4348
+ "instance"
4349
+ );
4350
+
4351
+ Expression instanceExpression =
4352
+ field.IsStatic ? null
4353
+ : field.DeclaringType.IsValueType
4354
+ ? Expression.Unbox(instanceParam, field.DeclaringType)
4355
+ : Expression.Convert(instanceParam, field.DeclaringType);
4356
+
4357
+ Expression fieldExpression = field.IsStatic
4358
+ ? Expression.Field(null, field)
4359
+ : Expression.Field(instanceExpression, field);
4360
+ Expression returnExpression = field.FieldType.IsValueType
4361
+ ? Expression.Convert(fieldExpression, typeof(object))
4362
+ : fieldExpression;
4363
+
4364
+ return Expression
4365
+ .Lambda<Func<object, object>>(returnExpression, instanceParam)
4366
+ .Compile();
4367
+ }
4368
+ catch
4369
+ {
4370
+ return null;
4371
+ }
4372
+ }
4373
+
4374
+ private static bool CanInlineAssignment(Type sourceType, Type targetType)
4375
+ {
4376
+ if (sourceType == targetType)
4377
+ {
4378
+ return true;
4379
+ }
4380
+
4381
+ if (!targetType.IsValueType)
4382
+ {
4383
+ if (sourceType.IsValueType)
4384
+ {
4385
+ return true;
4386
+ }
4387
+
4388
+ return targetType.IsAssignableFrom(sourceType);
4389
+ }
4390
+
4391
+ if (!sourceType.IsValueType)
4392
+ {
4393
+ return sourceType == typeof(object);
4394
+ }
4395
+
4396
+ return false;
4397
+ }
4398
+
4399
+ private static bool CanInlineReturnConversion(Type actualType, Type requestedType)
4400
+ {
4401
+ if (actualType == requestedType)
4402
+ {
4403
+ return true;
4404
+ }
4405
+
4406
+ if (requestedType.IsValueType)
4407
+ {
4408
+ if (!actualType.IsValueType)
4409
+ {
4410
+ return actualType == typeof(object);
4411
+ }
4412
+
4413
+ return false;
4414
+ }
4415
+
4416
+ if (actualType.IsValueType)
4417
+ {
4418
+ return true;
4419
+ }
4420
+
4421
+ return requestedType.IsAssignableFrom(actualType);
4422
+ }
4423
+
4424
+ private static Func<object, object> CreateCompiledPropertyGetter(PropertyInfo property)
4425
+ {
4426
+ try
4427
+ {
4428
+ MethodInfo getMethod = property.GetGetMethod(true);
4429
+ if (getMethod == null)
4430
+ {
4431
+ return property.GetValue;
4432
+ }
4433
+
4434
+ ParameterExpression instanceParam = Expression.Parameter(
4435
+ typeof(object),
4436
+ "instance"
4437
+ );
4438
+ Expression propertyExpression;
4439
+
4440
+ if (getMethod.IsStatic)
4441
+ {
4442
+ propertyExpression = Expression.Property(null, property);
4443
+ }
4444
+ else
4445
+ {
4446
+ Expression instanceExpression = property.DeclaringType.IsValueType
4447
+ ? Expression.Unbox(instanceParam, property.DeclaringType)
4448
+ : Expression.Convert(instanceParam, property.DeclaringType);
4449
+ propertyExpression = Expression.Property(instanceExpression, property);
4450
+ }
4451
+
4452
+ Expression returnExpression = property.PropertyType.IsValueType
4453
+ ? Expression.Convert(propertyExpression, typeof(object))
4454
+ : propertyExpression;
4455
+
4456
+ return Expression
4457
+ .Lambda<Func<object, object>>(returnExpression, instanceParam)
4458
+ .Compile();
4459
+ }
4460
+ catch
4461
+ {
4462
+ return property.GetValue;
4463
+ }
4464
+ }
4465
+
4466
+ private static Action<object, object> CreateCompiledPropertySetter(PropertyInfo property)
4467
+ {
4468
+ if (!ExpressionsEnabled)
4469
+ {
4470
+ return property.SetValue;
4471
+ }
4472
+
4473
+ try
4474
+ {
4475
+ MethodInfo setMethod = property.GetSetMethod(true);
4476
+ if (setMethod == null)
4477
+ {
4478
+ return property.SetValue;
4479
+ }
4480
+
4481
+ ParameterExpression instanceParam = Expression.Parameter(
4482
+ typeof(object),
4483
+ "instance"
4484
+ );
4485
+ ParameterExpression valueParam = Expression.Parameter(typeof(object), "value");
4486
+
4487
+ Expression valueExpression = property.PropertyType.IsValueType
4488
+ ? Expression.Unbox(valueParam, property.PropertyType)
4489
+ : Expression.Convert(valueParam, property.PropertyType);
4490
+
4491
+ Expression callExpression;
4492
+ if (setMethod.IsStatic)
4493
+ {
4494
+ callExpression = Expression.Call(setMethod, valueExpression);
4495
+ }
4496
+ else
4497
+ {
4498
+ Expression instanceExpression = property.DeclaringType.IsValueType
4499
+ ? Expression.Unbox(instanceParam, property.DeclaringType)
4500
+ : Expression.Convert(instanceParam, property.DeclaringType);
4501
+ callExpression = Expression.Call(
4502
+ instanceExpression,
4503
+ setMethod,
4504
+ valueExpression
4505
+ );
4506
+ }
4507
+
4508
+ return Expression
4509
+ .Lambda<Action<object, object>>(callExpression, instanceParam, valueParam)
4510
+ .Compile();
4511
+ }
4512
+ catch
4513
+ {
4514
+ return property.SetValue;
4515
+ }
4516
+ }
4517
+
4518
+ private static Func<TInstance, TValue> CreateCompiledTypedFieldGetter<TInstance, TValue>(
4519
+ FieldInfo field
4520
+ )
4521
+ {
4522
+ if (!ExpressionsEnabled)
4523
+ {
4524
+ return null;
4525
+ }
4526
+
4527
+ try
4528
+ {
4529
+ ParameterExpression instanceParam = Expression.Parameter(
4530
+ typeof(TInstance),
4531
+ "instance"
4532
+ );
4533
+ Expression fieldExpression;
4534
+ if (field.IsStatic)
4535
+ {
4536
+ fieldExpression = Expression.Field(null, field);
4537
+ }
4538
+ else
4539
+ {
4540
+ Expression instanceExpression = PrepareInstanceExpression(
4541
+ instanceParam,
4542
+ field.DeclaringType
4543
+ );
4544
+ fieldExpression = Expression.Field(instanceExpression, field);
4545
+ }
4546
+
4547
+ Expression body =
4548
+ field.FieldType == typeof(TValue)
4549
+ ? fieldExpression
4550
+ : Expression.Convert(fieldExpression, typeof(TValue));
4551
+
4552
+ return Expression.Lambda<Func<TInstance, TValue>>(body, instanceParam).Compile();
4553
+ }
4554
+ catch
4555
+ {
4556
+ return null;
4557
+ }
4558
+ }
4559
+
4560
+ private static FieldSetter<TInstance, TValue> CreateCompiledTypedFieldSetter<
4561
+ TInstance,
4562
+ TValue
4563
+ >(FieldInfo field)
4564
+ {
4565
+ if (!ExpressionsEnabled)
4566
+ {
4567
+ return null;
4568
+ }
4569
+
4570
+ if (!field.IsStatic && typeof(TInstance) != field.DeclaringType)
4571
+ {
4572
+ return null;
4573
+ }
4574
+
4575
+ try
4576
+ {
4577
+ ParameterExpression instanceParam = Expression.Parameter(
4578
+ typeof(TInstance).MakeByRefType(),
4579
+ "instance"
4580
+ );
4581
+ ParameterExpression valueParam = Expression.Parameter(typeof(TValue), "value");
4582
+
4583
+ Expression valueExpression =
4584
+ field.FieldType == typeof(TValue)
4585
+ ? valueParam
4586
+ : Expression.Convert(valueParam, field.FieldType);
4587
+
4588
+ Expression assignExpression;
4589
+ if (field.IsStatic)
4590
+ {
4591
+ assignExpression = Expression.Assign(
4592
+ Expression.Field(null, field),
4593
+ valueExpression
4594
+ );
4595
+ }
4596
+ else
4597
+ {
4598
+ Expression fieldAccess = Expression.Field(instanceParam, field);
4599
+ assignExpression = Expression.Assign(fieldAccess, valueExpression);
4600
+ }
4601
+
4602
+ return Expression
4603
+ .Lambda<FieldSetter<TInstance, TValue>>(
4604
+ assignExpression,
4605
+ instanceParam,
4606
+ valueParam
4607
+ )
4608
+ .Compile();
4609
+ }
4610
+ catch
4611
+ {
4612
+ return null;
4613
+ }
4614
+ }
4615
+
4616
+ private static Func<TValue> CreateCompiledTypedStaticFieldGetter<TValue>(FieldInfo field)
4617
+ {
4618
+ if (!ExpressionsEnabled)
4619
+ {
4620
+ return null;
4621
+ }
4622
+
4623
+ try
4624
+ {
4625
+ Expression body = Expression.Field(null, field);
4626
+ if (field.FieldType != typeof(TValue))
4627
+ {
4628
+ body = Expression.Convert(body, typeof(TValue));
4629
+ }
4630
+
4631
+ return Expression.Lambda<Func<TValue>>(body).Compile();
4632
+ }
4633
+ catch
4634
+ {
4635
+ return null;
4636
+ }
4637
+ }
4638
+
4639
+ private static Action<TValue> CreateCompiledTypedStaticFieldSetter<TValue>(FieldInfo field)
4640
+ {
4641
+ if (!ExpressionsEnabled)
4642
+ {
4643
+ return null;
4644
+ }
4645
+
4646
+ try
4647
+ {
4648
+ ParameterExpression valueParam = Expression.Parameter(typeof(TValue), "value");
4649
+ Expression valueExpression =
4650
+ field.FieldType == typeof(TValue)
4651
+ ? valueParam
4652
+ : Expression.Convert(valueParam, field.FieldType);
4653
+ Expression assignExpression = Expression.Assign(
4654
+ Expression.Field(null, field),
4655
+ valueExpression
4656
+ );
4657
+ return Expression.Lambda<Action<TValue>>(assignExpression, valueParam).Compile();
4658
+ }
4659
+ catch
4660
+ {
4661
+ return null;
4662
+ }
4663
+ }
4664
+
4665
+ private static Func<TInstance, TValue> CreateCompiledTypedPropertyGetter<TInstance, TValue>(
4666
+ PropertyInfo property,
4667
+ MethodInfo getMethod
4668
+ )
4669
+ {
4670
+ if (!ExpressionsEnabled)
4671
+ {
4672
+ return null;
4673
+ }
4674
+
4675
+ try
4676
+ {
4677
+ ParameterExpression instanceParam = Expression.Parameter(
4678
+ typeof(TInstance),
4679
+ "instance"
4680
+ );
4681
+
4682
+ Expression callExpression;
4683
+ if (getMethod.IsStatic)
4684
+ {
4685
+ callExpression = Expression.Call(getMethod);
4686
+ }
4687
+ else
4688
+ {
4689
+ Expression instanceExpression = PrepareInstanceExpression(
4690
+ instanceParam,
4691
+ property.DeclaringType
4692
+ );
4693
+ callExpression = Expression.Call(instanceExpression, getMethod);
4694
+ }
4695
+
4696
+ Expression body =
4697
+ property.PropertyType == typeof(TValue)
4698
+ ? callExpression
4699
+ : Expression.Convert(callExpression, typeof(TValue));
4700
+
4701
+ return Expression.Lambda<Func<TInstance, TValue>>(body, instanceParam).Compile();
4702
+ }
4703
+ catch
4704
+ {
4705
+ return null;
4706
+ }
4707
+ }
4708
+
4709
+ private static Action<TInstance, TValue> CreateCompiledTypedPropertySetter<
4710
+ TInstance,
4711
+ TValue
4712
+ >(PropertyInfo property, MethodInfo setMethod)
4713
+ {
4714
+ if (!ExpressionsEnabled)
4715
+ {
4716
+ return null;
4717
+ }
4718
+
4719
+ if (!setMethod.IsStatic && property.DeclaringType.IsValueType)
4720
+ {
4721
+ return null;
4722
+ }
4723
+
4724
+ try
4725
+ {
4726
+ ParameterExpression instanceParam = Expression.Parameter(
4727
+ typeof(TInstance),
4728
+ "instance"
4729
+ );
4730
+ ParameterExpression valueParam = Expression.Parameter(typeof(TValue), "value");
4731
+
4732
+ Expression valueExpression =
4733
+ property.PropertyType == typeof(TValue)
4734
+ ? valueParam
4735
+ : Expression.Convert(valueParam, property.PropertyType);
4736
+
4737
+ MethodCallExpression callExpression;
4738
+ if (setMethod.IsStatic)
4739
+ {
4740
+ callExpression = Expression.Call(setMethod, valueExpression);
4741
+ }
4742
+ else
4743
+ {
4744
+ Expression instanceExpression = PrepareInstanceExpression(
4745
+ instanceParam,
4746
+ property.DeclaringType
4747
+ );
4748
+ callExpression = Expression.Call(
4749
+ instanceExpression,
4750
+ setMethod,
4751
+ valueExpression
4752
+ );
4753
+ }
4754
+
4755
+ return Expression
4756
+ .Lambda<Action<TInstance, TValue>>(callExpression, instanceParam, valueParam)
4757
+ .Compile();
4758
+ }
4759
+ catch
4760
+ {
4761
+ return null;
4762
+ }
4763
+ }
4764
+
4765
+ private static Func<TValue> CreateCompiledTypedStaticPropertyGetter<TValue>(
4766
+ PropertyInfo property,
4767
+ MethodInfo getMethod
4768
+ )
4769
+ {
4770
+ if (!ExpressionsEnabled)
4771
+ {
4772
+ return null;
4773
+ }
4774
+
4775
+ try
4776
+ {
4777
+ Expression body = Expression.Call(getMethod);
4778
+ if (property.PropertyType != typeof(TValue))
4779
+ {
4780
+ body = Expression.Convert(body, typeof(TValue));
4781
+ }
4782
+
4783
+ return Expression.Lambda<Func<TValue>>(body).Compile();
4784
+ }
4785
+ catch
4786
+ {
4787
+ return null;
4788
+ }
4789
+ }
4790
+
4791
+ private static Action<TValue> CreateCompiledTypedStaticPropertySetter<TValue>(
4792
+ PropertyInfo property,
4793
+ MethodInfo setMethod
4794
+ )
4795
+ {
4796
+ if (!ExpressionsEnabled)
4797
+ {
4798
+ return null;
4799
+ }
4800
+
4801
+ try
4802
+ {
4803
+ ParameterExpression valueParam = Expression.Parameter(typeof(TValue), "value");
4804
+
4805
+ Expression valueExpression =
4806
+ property.PropertyType == typeof(TValue)
4807
+ ? valueParam
4808
+ : Expression.Convert(valueParam, property.PropertyType);
4809
+
4810
+ MethodCallExpression callExpression = Expression.Call(setMethod, valueExpression);
4811
+
4812
+ return Expression.Lambda<Action<TValue>>(callExpression, valueParam).Compile();
4813
+ }
4814
+ catch
4815
+ {
4816
+ return null;
4817
+ }
4818
+ }
4819
+
4820
+ private static Expression PrepareInstanceExpression(
4821
+ ParameterExpression instanceParameter,
4822
+ Type declaringType
4823
+ )
4824
+ {
4825
+ if (instanceParameter.Type == declaringType)
4826
+ {
4827
+ return instanceParameter;
4828
+ }
4829
+
4830
+ return Expression.Convert(instanceParameter, declaringType);
4831
+ }
4832
+
4833
+ #if EMIT_DYNAMIC_IL
4834
+ private static void EmitAssignmentConversion(
4835
+ ILGenerator il,
4836
+ Type sourceType,
4837
+ Type targetType
4838
+ )
4839
+ {
4840
+ if (targetType == sourceType)
4841
+ {
4842
+ return;
4843
+ }
4844
+
4845
+ if (targetType.IsValueType)
4846
+ {
4847
+ if (!sourceType.IsValueType)
4848
+ {
4849
+ il.Emit(OpCodes.Unbox_Any, targetType);
4850
+ }
4851
+ }
4852
+ else
4853
+ {
4854
+ if (sourceType.IsValueType)
4855
+ {
4856
+ il.Emit(OpCodes.Box, sourceType);
4857
+ if (targetType != typeof(object))
4858
+ {
4859
+ il.Emit(OpCodes.Castclass, targetType);
4860
+ }
4861
+ }
4862
+ else if (!targetType.IsAssignableFrom(sourceType))
4863
+ {
4864
+ il.Emit(OpCodes.Castclass, targetType);
4865
+ }
4866
+ }
4867
+ }
4868
+
4869
+ private static void EmitReturnConversion(
4870
+ ILGenerator il,
4871
+ Type actualType,
4872
+ Type requestedType
4873
+ )
4874
+ {
4875
+ if (requestedType == actualType)
4876
+ {
4877
+ return;
4878
+ }
4879
+
4880
+ if (requestedType.IsValueType)
4881
+ {
4882
+ if (!actualType.IsValueType)
4883
+ {
4884
+ il.Emit(OpCodes.Unbox_Any, requestedType);
4885
+ }
4886
+ }
4887
+ else
4888
+ {
4889
+ if (actualType.IsValueType)
4890
+ {
4891
+ il.Emit(OpCodes.Box, actualType);
4892
+ if (requestedType != typeof(object))
4893
+ {
4894
+ il.Emit(OpCodes.Castclass, requestedType);
4895
+ }
4896
+ }
4897
+ else if (!requestedType.IsAssignableFrom(actualType))
4898
+ {
4899
+ il.Emit(OpCodes.Castclass, requestedType);
4900
+ }
4901
+ }
4902
+ }
4903
+
4904
+ private static Func<object> BuildParameterlessConstructorIL(ConstructorInfo constructor)
4905
+ {
4906
+ DynamicMethod dynamicMethod = new(
4907
+ $"New_{constructor.DeclaringType.Name}_Obj",
4908
+ typeof(object),
4909
+ Type.EmptyTypes,
4910
+ constructor.DeclaringType,
4911
+ true
4912
+ );
4913
+ ILGenerator il = dynamicMethod.GetILGenerator();
4914
+ il.Emit(OpCodes.Newobj, constructor);
4915
+ if (constructor.DeclaringType.IsValueType)
4916
+ {
4917
+ il.Emit(OpCodes.Box, constructor.DeclaringType);
4918
+ }
4919
+ il.Emit(OpCodes.Ret);
4920
+ return (Func<object>)dynamicMethod.CreateDelegate(typeof(Func<object>));
4921
+ }
4922
+
4923
+ private static Func<T> BuildTypedParameterlessConstructorIL<T>(ConstructorInfo constructor)
4924
+ {
4925
+ DynamicMethod dynamicMethod = new(
4926
+ $"New_{constructor.DeclaringType.Name}_{typeof(T).Name}",
4927
+ typeof(T),
4928
+ Type.EmptyTypes,
4929
+ constructor.DeclaringType,
4930
+ true
4931
+ );
4932
+ ILGenerator il = dynamicMethod.GetILGenerator();
4933
+ il.Emit(OpCodes.Newobj, constructor);
4934
+
4935
+ Type declaringType = constructor.DeclaringType;
4936
+ if (declaringType != typeof(T))
4937
+ {
4938
+ if (declaringType.IsValueType)
4939
+ {
4940
+ il.Emit(OpCodes.Box, declaringType);
4941
+ if (typeof(T) != typeof(object))
4942
+ {
4943
+ il.Emit(OpCodes.Castclass, typeof(T));
4944
+ }
4945
+ }
4946
+ else if (!typeof(T).IsAssignableFrom(declaringType))
4947
+ {
4948
+ il.Emit(OpCodes.Castclass, typeof(T));
4949
+ }
4950
+ }
4951
+
4952
+ il.Emit(OpCodes.Ret);
4953
+ return (Func<T>)dynamicMethod.CreateDelegate(typeof(Func<T>));
4954
+ }
4955
+
4956
+ private static Func<object, object[], object> BuildIndexerGetterIL(PropertyInfo property)
4957
+ {
4958
+ MethodInfo getter =
4959
+ property.GetGetMethod(true)
4960
+ ?? throw new ArgumentException("Indexer has no getter", nameof(property));
4961
+
4962
+ DynamicMethod dynamicMethod = new(
4963
+ $"GetIndexer_{property.DeclaringType.Name}_{property.Name}",
4964
+ typeof(object),
4965
+ new[] { typeof(object), typeof(object[]) },
4966
+ property.DeclaringType,
4967
+ true
4968
+ );
4969
+ ILGenerator il = dynamicMethod.GetILGenerator();
4970
+ bool isStatic = getter.IsStatic;
4971
+ if (!isStatic)
4972
+ {
4973
+ il.Emit(OpCodes.Ldarg_0);
4974
+ il.Emit(
4975
+ property.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass,
4976
+ property.DeclaringType
4977
+ );
4978
+ }
4979
+
4980
+ ParameterInfo[] indices = property.GetIndexParameters();
4981
+ for (int i = 0; i < indices.Length; i++)
4982
+ {
4983
+ il.Emit(OpCodes.Ldarg_1);
4984
+ il.Emit(OpCodes.Ldc_I4, i);
4985
+ il.Emit(OpCodes.Ldelem_Ref);
4986
+ EmitAssignmentConversion(il, typeof(object), indices[i].ParameterType);
4987
+ }
4988
+
4989
+ il.Emit(
4990
+ getter.IsStatic ? OpCodes.Call
4991
+ : property.DeclaringType.IsValueType ? OpCodes.Call
4992
+ : OpCodes.Callvirt,
4993
+ getter
4994
+ );
4995
+ EmitReturnConversion(il, property.PropertyType, typeof(object));
4996
+ il.Emit(OpCodes.Ret);
4997
+ return (Func<object, object[], object>)
4998
+ dynamicMethod.CreateDelegate(typeof(Func<object, object[], object>));
4999
+ }
5000
+
5001
+ private static Action<object, object, object[]> BuildIndexerSetterIL(PropertyInfo property)
5002
+ {
5003
+ MethodInfo setter =
5004
+ property.GetSetMethod(true)
5005
+ ?? throw new ArgumentException("Indexer has no setter", nameof(property));
5006
+
5007
+ DynamicMethod dynamicMethod = new(
5008
+ $"SetIndexer_{property.DeclaringType.Name}_{property.Name}",
5009
+ typeof(void),
5010
+ new[] { typeof(object), typeof(object), typeof(object[]) },
5011
+ property.DeclaringType,
5012
+ true
5013
+ );
5014
+ ILGenerator il = dynamicMethod.GetILGenerator();
5015
+ bool isStatic = setter.IsStatic;
5016
+ if (!isStatic)
5017
+ {
5018
+ il.Emit(OpCodes.Ldarg_0);
5019
+ il.Emit(
5020
+ property.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass,
5021
+ property.DeclaringType
5022
+ );
5023
+ }
5024
+
5025
+ ParameterInfo[] indices = property.GetIndexParameters();
5026
+ for (int i = 0; i < indices.Length; i++)
5027
+ {
5028
+ il.Emit(OpCodes.Ldarg_2);
5029
+ il.Emit(OpCodes.Ldc_I4, i);
5030
+ il.Emit(OpCodes.Ldelem_Ref);
5031
+ EmitAssignmentConversion(il, typeof(object), indices[i].ParameterType);
5032
+ }
5033
+
5034
+ il.Emit(OpCodes.Ldarg_1);
5035
+ EmitAssignmentConversion(il, typeof(object), property.PropertyType);
5036
+ il.Emit(
5037
+ setter.IsStatic ? OpCodes.Call
5038
+ : property.DeclaringType.IsValueType ? OpCodes.Call
5039
+ : OpCodes.Callvirt,
5040
+ setter
5041
+ );
5042
+ il.Emit(OpCodes.Ret);
5043
+ return (Action<object, object, object[]>)
5044
+ dynamicMethod.CreateDelegate(typeof(Action<object, object, object[]>));
5045
+ }
5046
+
5047
+ private static Func<TInstance, TValue> BuildTypedFieldGetterIL<TInstance, TValue>(
5048
+ FieldInfo field
5049
+ )
5050
+ {
5051
+ DynamicMethod dynamicMethod = new(
5052
+ $"GetGeneric{field.DeclaringType.Name}_{field.Name}",
5053
+ typeof(TValue),
5054
+ new[] { typeof(TInstance) },
5055
+ field.DeclaringType,
5056
+ true
5057
+ );
5058
+ ILGenerator il = dynamicMethod.GetILGenerator();
5059
+
5060
+ if (field.IsStatic)
5061
+ {
5062
+ il.Emit(OpCodes.Ldsfld, field);
5063
+ }
5064
+ else
5065
+ {
5066
+ if (typeof(TInstance).IsValueType)
5067
+ {
5068
+ il.Emit(OpCodes.Ldarga_S, 0);
5069
+ }
5070
+ else
5071
+ {
5072
+ il.Emit(OpCodes.Ldarg_0);
5073
+ }
5074
+
5075
+ if (field.DeclaringType != typeof(TInstance))
5076
+ {
5077
+ il.Emit(
5078
+ field.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass,
5079
+ field.DeclaringType
5080
+ );
5081
+ }
5082
+
5083
+ il.Emit(OpCodes.Ldfld, field);
5084
+ }
5085
+
5086
+ EmitReturnConversion(il, field.FieldType, typeof(TValue));
5087
+ il.Emit(OpCodes.Ret);
5088
+ return (Func<TInstance, TValue>)
5089
+ dynamicMethod.CreateDelegate(typeof(Func<TInstance, TValue>));
5090
+ }
5091
+
5092
+ private static FieldSetter<TInstance, TValue> BuildTypedFieldSetterIL<TInstance, TValue>(
5093
+ FieldInfo field
5094
+ )
5095
+ {
5096
+ DynamicMethod dynamicMethod = new(
5097
+ $"SetFieldGeneric{field.DeclaringType.Name}_{field.Name}",
5098
+ typeof(void),
5099
+ new[] { typeof(TInstance).MakeByRefType(), typeof(TValue) },
5100
+ field.Module,
5101
+ true
5102
+ );
5103
+ ILGenerator il = dynamicMethod.GetILGenerator();
5104
+
5105
+ if (field.IsStatic)
5106
+ {
5107
+ il.Emit(OpCodes.Ldarg_1);
5108
+ EmitAssignmentConversion(il, typeof(TValue), field.FieldType);
5109
+ il.Emit(OpCodes.Stsfld, field);
5110
+ }
5111
+ else
5112
+ {
5113
+ if (typeof(TInstance).IsValueType)
5114
+ {
5115
+ il.Emit(OpCodes.Ldarg_0);
5116
+ }
5117
+ else
5118
+ {
5119
+ il.Emit(OpCodes.Ldarg_0);
5120
+ il.Emit(OpCodes.Ldind_Ref);
5121
+ }
5122
+
5123
+ if (field.DeclaringType != typeof(TInstance))
5124
+ {
5125
+ il.Emit(
5126
+ field.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass,
5127
+ field.DeclaringType
5128
+ );
5129
+ }
5130
+
5131
+ il.Emit(OpCodes.Ldarg_1);
5132
+ EmitAssignmentConversion(il, typeof(TValue), field.FieldType);
5133
+ il.Emit(OpCodes.Stfld, field);
4812
5134
  }
5135
+
5136
+ il.Emit(OpCodes.Ret);
5137
+ return (FieldSetter<TInstance, TValue>)
5138
+ dynamicMethod.CreateDelegate(typeof(FieldSetter<TInstance, TValue>));
4813
5139
  }
4814
5140
 
4815
- private static Func<T> CreateCompiledParameterlessConstructor<T>(
4816
- ConstructorInfo constructor
4817
- )
5141
+ private static Func<TValue> BuildTypedStaticFieldGetterIL<TValue>(FieldInfo field)
4818
5142
  {
4819
- if (!CanCompileExpressions)
4820
- {
4821
- return CreateDelegateParameterlessConstructor<T>(constructor)
4822
- ?? (() => (T)Activator.CreateInstance(typeof(T)));
4823
- }
5143
+ DynamicMethod dynamicMethod = new(
5144
+ $"GetStaticTyped{field.DeclaringType.Name}_{field.Name}",
5145
+ typeof(TValue),
5146
+ Type.EmptyTypes,
5147
+ field.DeclaringType,
5148
+ true
5149
+ );
5150
+ ILGenerator il = dynamicMethod.GetILGenerator();
5151
+ il.Emit(OpCodes.Ldsfld, field);
5152
+ EmitReturnConversion(il, field.FieldType, typeof(TValue));
5153
+ il.Emit(OpCodes.Ret);
5154
+ return (Func<TValue>)dynamicMethod.CreateDelegate(typeof(Func<TValue>));
5155
+ }
4824
5156
 
4825
- try
4826
- {
4827
- Expression newExpression = Expression.New(constructor);
4828
- return Expression.Lambda<Func<T>>(newExpression).Compile();
4829
- }
4830
- catch
4831
- {
4832
- return () => (T)Activator.CreateInstance(typeof(T));
4833
- }
5157
+ private static Action<TValue> BuildTypedStaticFieldSetterIL<TValue>(FieldInfo field)
5158
+ {
5159
+ DynamicMethod dynamicMethod = new(
5160
+ $"SetStaticTyped{field.DeclaringType.Name}_{field.Name}",
5161
+ typeof(void),
5162
+ new[] { typeof(TValue) },
5163
+ field.Module,
5164
+ true
5165
+ );
5166
+ ILGenerator il = dynamicMethod.GetILGenerator();
5167
+ il.Emit(OpCodes.Ldarg_0);
5168
+ EmitAssignmentConversion(il, typeof(TValue), field.FieldType);
5169
+ il.Emit(OpCodes.Stsfld, field);
5170
+ il.Emit(OpCodes.Ret);
5171
+ return (Action<TValue>)dynamicMethod.CreateDelegate(typeof(Action<TValue>));
4834
5172
  }
4835
5173
 
4836
- private static Func<T> CreateCompiledGenericParameterlessConstructor<T>(
4837
- Type constructedType,
4838
- ConstructorInfo constructor
5174
+ private static Func<TInstance, TValue> BuildTypedPropertyGetterIL<TInstance, TValue>(
5175
+ PropertyInfo property,
5176
+ MethodInfo getMethod
4839
5177
  )
4840
5178
  {
4841
- if (!CanCompileExpressions)
4842
- {
4843
- return () => (T)Activator.CreateInstance(constructedType);
4844
- }
4845
-
4846
- try
4847
- {
4848
- Expression newExpression = Expression.New(constructor);
4849
- Expression convertExpression =
4850
- constructedType.IsValueType && typeof(T) == typeof(object)
4851
- ? Expression.Convert(newExpression, typeof(object))
4852
- : newExpression;
5179
+ DynamicMethod dynamicMethod = new(
5180
+ $"GetGeneric{property.DeclaringType.Name}_{property.Name}",
5181
+ typeof(TValue),
5182
+ new[] { typeof(TInstance) },
5183
+ property.DeclaringType,
5184
+ true
5185
+ );
4853
5186
 
4854
- return Expression.Lambda<Func<T>>(convertExpression).Compile();
4855
- }
4856
- catch
4857
- {
4858
- return () => (T)Activator.CreateInstance(constructedType);
4859
- }
4860
- }
5187
+ ILGenerator il = dynamicMethod.GetILGenerator();
4861
5188
 
4862
- private static Func<object, object> CreateCompiledFieldGetter(FieldInfo field)
4863
- {
4864
- if (!CanCompileExpressions)
5189
+ if (getMethod.IsStatic)
4865
5190
  {
4866
- return CreateDelegateFieldGetter(field) ?? field.GetValue;
5191
+ il.Emit(OpCodes.Call, getMethod);
4867
5192
  }
4868
-
4869
- try
5193
+ else
4870
5194
  {
4871
- ParameterExpression instanceParam = Expression.Parameter(
4872
- typeof(object),
4873
- "instance"
4874
- );
5195
+ if (typeof(TInstance).IsValueType)
5196
+ {
5197
+ il.Emit(OpCodes.Ldarga_S, 0);
5198
+ }
5199
+ else
5200
+ {
5201
+ il.Emit(OpCodes.Ldarg_0);
5202
+ }
4875
5203
 
4876
- Expression instanceExpression = field.DeclaringType.IsValueType
4877
- ? Expression.Unbox(instanceParam, field.DeclaringType)
4878
- : Expression.Convert(instanceParam, field.DeclaringType);
5204
+ if (property.DeclaringType != typeof(TInstance))
5205
+ {
5206
+ il.Emit(
5207
+ property.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass,
5208
+ property.DeclaringType
5209
+ );
5210
+ }
4879
5211
 
4880
- Expression fieldExpression = Expression.Field(instanceExpression, field);
5212
+ il.Emit(
5213
+ property.DeclaringType.IsValueType ? OpCodes.Call : OpCodes.Callvirt,
5214
+ getMethod
5215
+ );
5216
+ }
4881
5217
 
4882
- Expression returnExpression = field.FieldType.IsValueType
4883
- ? Expression.Convert(fieldExpression, typeof(object))
4884
- : fieldExpression;
5218
+ EmitReturnConversion(il, property.PropertyType, typeof(TValue));
4885
5219
 
4886
- return Expression
4887
- .Lambda<Func<object, object>>(returnExpression, instanceParam)
4888
- .Compile();
4889
- }
4890
- catch
4891
- {
4892
- return field.GetValue;
4893
- }
5220
+ il.Emit(OpCodes.Ret);
5221
+ return (Func<TInstance, TValue>)
5222
+ dynamicMethod.CreateDelegate(typeof(Func<TInstance, TValue>));
4894
5223
  }
4895
5224
 
4896
- private static Func<object, object> CreateCompiledPropertyGetter(PropertyInfo property)
5225
+ private static Action<TInstance, TValue> BuildTypedPropertySetterIL<TInstance, TValue>(
5226
+ PropertyInfo property,
5227
+ MethodInfo setMethod
5228
+ )
4897
5229
  {
4898
- if (!CanCompileExpressions)
5230
+ DynamicMethod dynamicMethod = new(
5231
+ $"SetPropertyGeneric{property.DeclaringType.Name}_{property.Name}",
5232
+ typeof(void),
5233
+ new[] { typeof(TInstance), typeof(TValue) },
5234
+ property.DeclaringType,
5235
+ true
5236
+ );
5237
+ ILGenerator il = dynamicMethod.GetILGenerator();
5238
+
5239
+ if (setMethod.IsStatic)
4899
5240
  {
4900
- return CreateDelegatePropertyGetter(property) ?? property.GetValue;
5241
+ il.Emit(OpCodes.Ldarg_1);
5242
+ EmitAssignmentConversion(il, typeof(TValue), property.PropertyType);
5243
+ il.Emit(OpCodes.Call, setMethod);
4901
5244
  }
4902
-
4903
- try
5245
+ else
4904
5246
  {
4905
- MethodInfo getMethod = property.GetGetMethod(true);
4906
- if (getMethod == null)
5247
+ if (typeof(TInstance).IsValueType)
4907
5248
  {
4908
- return property.GetValue;
5249
+ il.Emit(OpCodes.Ldarga_S, 0);
5250
+ }
5251
+ else
5252
+ {
5253
+ il.Emit(OpCodes.Ldarg_0);
5254
+ }
5255
+ if (property.DeclaringType != typeof(TInstance))
5256
+ {
5257
+ il.Emit(
5258
+ property.DeclaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass,
5259
+ property.DeclaringType
5260
+ );
4909
5261
  }
4910
5262
 
4911
- ParameterExpression instanceParam = Expression.Parameter(
4912
- typeof(object),
4913
- "instance"
5263
+ il.Emit(OpCodes.Ldarg_1);
5264
+ EmitAssignmentConversion(il, typeof(TValue), property.PropertyType);
5265
+
5266
+ il.Emit(
5267
+ property.DeclaringType.IsValueType ? OpCodes.Call : OpCodes.Callvirt,
5268
+ setMethod
4914
5269
  );
5270
+ }
4915
5271
 
4916
- Expression instanceExpression = property.DeclaringType.IsValueType
4917
- ? Expression.Unbox(instanceParam, property.DeclaringType)
4918
- : Expression.Convert(instanceParam, property.DeclaringType);
5272
+ il.Emit(OpCodes.Ret);
5273
+ return (Action<TInstance, TValue>)
5274
+ dynamicMethod.CreateDelegate(typeof(Action<TInstance, TValue>));
5275
+ }
4919
5276
 
4920
- Expression propertyExpression = Expression.Property(instanceExpression, property);
5277
+ private static Func<TValue> BuildTypedStaticPropertyGetterIL<TValue>(
5278
+ PropertyInfo property,
5279
+ MethodInfo getMethod
5280
+ )
5281
+ {
5282
+ DynamicMethod dynamicMethod = new(
5283
+ $"GetStaticTyped{property.DeclaringType.Name}_{property.Name}",
5284
+ typeof(TValue),
5285
+ Type.EmptyTypes,
5286
+ property.DeclaringType,
5287
+ true
5288
+ );
5289
+ ILGenerator il = dynamicMethod.GetILGenerator();
5290
+ il.Emit(OpCodes.Call, getMethod);
5291
+ EmitReturnConversion(il, property.PropertyType, typeof(TValue));
4921
5292
 
4922
- Expression returnExpression = property.PropertyType.IsValueType
4923
- ? Expression.Convert(propertyExpression, typeof(object))
4924
- : propertyExpression;
5293
+ il.Emit(OpCodes.Ret);
5294
+ return (Func<TValue>)dynamicMethod.CreateDelegate(typeof(Func<TValue>));
5295
+ }
4925
5296
 
4926
- return Expression
4927
- .Lambda<Func<object, object>>(returnExpression, instanceParam)
4928
- .Compile();
4929
- }
4930
- catch
4931
- {
4932
- return property.GetValue;
4933
- }
5297
+ private static Action<TValue> BuildTypedStaticPropertySetterIL<TValue>(
5298
+ PropertyInfo property,
5299
+ MethodInfo setMethod
5300
+ )
5301
+ {
5302
+ DynamicMethod dynamicMethod = new(
5303
+ $"SetStaticTyped{property.DeclaringType.Name}_{property.Name}",
5304
+ typeof(void),
5305
+ new[] { typeof(TValue) },
5306
+ property.DeclaringType,
5307
+ true
5308
+ );
5309
+ ILGenerator il = dynamicMethod.GetILGenerator();
5310
+ il.Emit(OpCodes.Ldarg_0);
5311
+ EmitAssignmentConversion(il, typeof(TValue), property.PropertyType);
5312
+ il.Emit(OpCodes.Call, setMethod);
5313
+ il.Emit(OpCodes.Ret);
5314
+ return (Action<TValue>)dynamicMethod.CreateDelegate(typeof(Action<TValue>));
4934
5315
  }
5316
+ #endif
4935
5317
 
4936
5318
  private static Func<object> CreateCompiledStaticFieldGetter(FieldInfo field)
4937
5319
  {
4938
- if (!CanCompileExpressions)
5320
+ if (!ExpressionsEnabled)
4939
5321
  {
4940
5322
  return () => field.GetValue(null);
4941
5323
  }
@@ -4958,7 +5340,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4958
5340
 
4959
5341
  private static Action<object, object> CreateCompiledFieldSetter(FieldInfo field)
4960
5342
  {
4961
- if (!CanCompileExpressions)
5343
+ if (!ExpressionsEnabled)
4962
5344
  {
4963
5345
  return field.SetValue;
4964
5346
  }
@@ -4996,7 +5378,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
4996
5378
 
4997
5379
  private static Action<object> CreateCompiledStaticFieldSetter(FieldInfo field)
4998
5380
  {
4999
- if (!CanCompileExpressions)
5381
+ if (!ExpressionsEnabled)
5000
5382
  {
5001
5383
  return value => field.SetValue(null, value);
5002
5384
  }
@@ -5188,111 +5570,23 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
5188
5570
  }
5189
5571
  }
5190
5572
 
5191
- private static Func<object, object> CreateDelegateFieldGetter(FieldInfo field)
5192
- {
5193
- try
5194
- {
5195
- if (field.IsStatic)
5196
- {
5197
- // For static fields, create a simple wrapper
5198
- return instance => field.GetValue(null);
5199
- }
5200
-
5201
- // For instance fields, we can't easily create delegates, so use optimized wrapper
5202
- return instance => field.GetValue(instance);
5203
- }
5204
- catch
5205
- {
5206
- return null;
5207
- }
5208
- }
5209
-
5210
- private static Func<object, object> CreateDelegatePropertyGetter(PropertyInfo property)
5573
+ private static Func<object> CreateDelegateParameterlessConstructor(
5574
+ ConstructorInfo constructor
5575
+ )
5211
5576
  {
5212
5577
  try
5213
5578
  {
5214
- MethodInfo getMethod = property.GetGetMethod(true);
5215
- if (getMethod == null)
5579
+ if (constructor.GetParameters().Length == 0)
5216
5580
  {
5217
- return null;
5581
+ return () => constructor.Invoke(null);
5218
5582
  }
5219
5583
 
5220
- if (getMethod.IsStatic)
5221
- {
5222
- Type funcType = typeof(Func<>).MakeGenericType(property.PropertyType);
5223
- Delegate getter = Delegate.CreateDelegate(funcType, getMethod);
5224
- return instance => getter.DynamicInvoke();
5225
- }
5226
- else
5227
- {
5228
- Type funcType = typeof(Func<,>).MakeGenericType(
5229
- property.DeclaringType,
5230
- property.PropertyType
5231
- );
5232
- Delegate getter = Delegate.CreateDelegate(funcType, getMethod);
5233
- return instance => getter.DynamicInvoke(instance);
5234
- }
5235
- }
5236
- catch
5237
- {
5238
5584
  return null;
5239
5585
  }
5240
- }
5241
-
5242
- private static Func<object, object> CreateGenericFieldGetter(FieldInfo field)
5243
- {
5244
- try
5245
- {
5246
- // For now, just use direct field access - it's already reasonably fast
5247
- if (field.IsStatic)
5248
- {
5249
- return instance => field.GetValue(null);
5250
- }
5251
-
5252
- return instance => field.GetValue(instance);
5253
- }
5254
5586
  catch
5255
5587
  {
5256
5588
  return null;
5257
5589
  }
5258
5590
  }
5259
-
5260
- private static Type GetActionType(Type[] parameterTypes)
5261
- {
5262
- switch (parameterTypes.Length)
5263
- {
5264
- case 0:
5265
- return typeof(Action);
5266
- case 1:
5267
- return typeof(Action<>).MakeGenericType(parameterTypes);
5268
- case 2:
5269
- return typeof(Action<,>).MakeGenericType(parameterTypes);
5270
- case 3:
5271
- return typeof(Action<,,>).MakeGenericType(parameterTypes);
5272
- case 4:
5273
- return typeof(Action<,,,>).MakeGenericType(parameterTypes);
5274
- default:
5275
- return null;
5276
- }
5277
- }
5278
-
5279
- private static Type GetFuncType(Type[] typeArgs)
5280
- {
5281
- switch (typeArgs.Length)
5282
- {
5283
- case 1:
5284
- return typeof(Func<>).MakeGenericType(typeArgs);
5285
- case 2:
5286
- return typeof(Func<,>).MakeGenericType(typeArgs);
5287
- case 3:
5288
- return typeof(Func<,,>).MakeGenericType(typeArgs);
5289
- case 4:
5290
- return typeof(Func<,,,>).MakeGenericType(typeArgs);
5291
- case 5:
5292
- return typeof(Func<,,,,>).MakeGenericType(typeArgs);
5293
- default:
5294
- return null;
5295
- }
5296
- }
5297
5591
  }
5298
5592
  }