From 0837d0c423b72e50883903e30f606b45281835e8 Mon Sep 17 00:00:00 2001 From: Mike Marynowski Date: Fri, 9 Feb 2024 05:26:35 -0500 Subject: [PATCH] More optimizations and cleanup --- README.md | 174 +++++++++-------- Source/Directory.Build.props | 30 +-- .../DefaultActivator.cs | 66 +++++-- .../ObjectFactory.cs | 177 ++++++++++++------ .../Singulink.Reflection.ObjectFactory.csproj | 70 +++---- 5 files changed, 319 insertions(+), 198 deletions(-) diff --git a/README.md b/README.md index 1dd9d6e..2de8eca 100644 --- a/README.md +++ b/README.md @@ -29,158 +29,180 @@ You can view the API on [FuGet](https://www.fuget.org/packages/Singulink.Reflect ## Benchmarks -**.NET 8** -``` +Entries that end in `_Activator` use `System.Activator` and the entries below that ending in `_ObjectFactory` use this library for comparison. + +### .NET 8 + +#### Reference Types + | Method | Mean | Error | StdDev | Gen0 | Allocated | |----------------------------------------- |------------:|----------:|----------:|-------:|----------:| -| Activator_Object | 14.9644 ns | 0.2901 ns | 0.3341 ns | 0.0057 | 24 B | -| ObjectFactory_Object | 6.5780 ns | 0.0587 ns | 0.0549 ns | 0.0057 | 24 B | -| ObjectFactory_Object_Delegate | 6.3640 ns | 0.0508 ns | 0.0451 ns | 0.0057 | 24 B | -|----------------------------------------- |------------:|----------:|----------:|-------:|----------:| -| Activator_ObjectPrivateCtor | 11.9688 ns | 0.0894 ns | 0.0793 ns | 0.0057 | 24 B | -| ObjectFactory_ObjectPrivateCtor | 6.6406 ns | 0.0857 ns | 0.0760 ns | 0.0057 | 24 B | -| ObjectFactory_ObjectPrivateCtor_Delegate | 6.3639 ns | 0.0569 ns | 0.0532 ns | 0.0057 | 24 B | -|----------------------------------------- |------------:|----------:|----------:|-------:|----------:| -| Activator_ObjectWithParam | 285.5964 ns | 2.1783 ns | 1.9310 ns | 0.0858 | 360 B | -| ObjectFactory_ObjectWithParam_Delegate | 6.5055 ns | 0.1480 ns | 0.1384 ns | 0.0057 | 24 B | -|----------------------------------------- |------------:|----------:|----------:|-------:|----------:| -| Activator_Struct | 0.1714 ns | 0.0382 ns | 0.0357 ns | - | - | -| ObjectFactory_Struct | 0.1339 ns | 0.0159 ns | 0.0149 ns | - | - | -| ObjectFactory_Struct_Delegate | 0.4148 ns | 0.0089 ns | 0.0070 ns | - | - | -|----------------------------------------- |------------:|----------:|----------:|-------:|----------:| -| Activator_StructWithParams | 277.3095 ns | 1.3876 ns | 1.2300 ns | 0.0858 | 360 B | -| ObjectFactory_StructWithParams_Delegate | 1.7756 ns | 0.0062 ns | 0.0051 ns | - | - | -``` +| Object_Activator | 14.0557 ns | 0.3502 ns | 0.4429 ns | 0.0057 | 24 B | +| Object_ObjectFactory | 6.4743 ns | 0.0699 ns | 0.0653 ns | 0.0057 | 24 B | +| Object_ObjectFactory_Delegate | 6.2483 ns | 0.1278 ns | 0.1067 ns | 0.0057 | 24 B | +| --- +| ObjectPrivateCtor_Activator | 12.3806 ns | 0.1191 ns | 0.1114 ns | 0.0057 | 24 B | +| ObjectPrivateCtor_ObjectFactory | 6.5123 ns | 0.1423 ns | 0.1261 ns | 0.0057 | 24 B | +| ObjectPrivateCtor_ObjectFactory_Delegate | 6.2050 ns | 0.1144 ns | 0.1070 ns | 0.0057 | 24 B | +| --- +| ObjectWithParam_Activator | 290.2104 ns | 2.2835 ns | 2.0242 ns | 0.0858 | 360 B | +| ObjectWithParam_ObjectFactory_Delegate | 6.1892 ns | 0.0906 ns | 0.0707 ns | 0.0057 | 24 B | + +#### Value Types -**.NET 8 Native AOT** -``` | Method | Mean | Error | StdDev | Gen0 | Allocated | |----------------------------------------- |------------:|----------:|----------:|-------:|----------:| -| Activator_Object | 5.8690 ns | 0.1876 ns | 0.2920 ns | 0.0057 | 24 B | -| ObjectFactory_Object | 5.4405 ns | 0.1007 ns | 0.0942 ns | 0.0057 | 24 B | -| ObjectFactory_Object_Delegate | 5.8968 ns | 0.0417 ns | 0.0390 ns | 0.0057 | 24 B | -|----------------------------------------- |------------:|----------:|----------:|-------:|----------:| -| Activator_ObjectPrivateCtor | 93.0655 ns | 0.7956 ns | 0.7052 ns | 0.0057 | 24 B | -| ObjectFactory_ObjectPrivateCtor | 77.0504 ns | 0.6919 ns | 0.6134 ns | 0.0324 | 136 B | -| ObjectFactory_ObjectPrivateCtor_Delegate | 76.3781 ns | 0.9018 ns | 0.8435 ns | 0.0324 | 136 B | -|----------------------------------------- |------------:|----------:|----------:|-------:|----------:| -| Activator_ObjectWithParam | 365.1306 ns | 2.5832 ns | 2.4163 ns | 0.0763 | 320 B | -| ObjectFactory_ObjectWithParam_Delegate | 125.5081 ns | 1.9267 ns | 1.8022 ns | 0.0496 | 208 B | +| Struct_Activator | 0.1334 ns | 0.0067 ns | 0.0056 ns | - | - | +| Struct_ObjectFactory | 0.1406 ns | 0.0072 ns | 0.0063 ns | - | - | +| Struct_ObjectFactory_Delegate | 0.1395 ns | 0.0121 ns | 0.0107 ns | - | - | +| --- +| StructWithParams_Activator | 292.7251 ns | 3.2207 ns | 3.0127 ns | 0.0858 | 360 B | +| StructWithParams_ObjectFactory_Delegate | 1.7616 ns | 0.0289 ns | 0.0270 ns | - | - | + +### .NET 8 Native AOT + +#### Reference Types + +| Method | Mean | Error | StdDev | Gen0 | Allocated | |----------------------------------------- |------------:|----------:|----------:|-------:|----------:| -| Activator_Struct | 0.1400 ns | 0.0131 ns | 0.0116 ns | - | - | -| ObjectFactory_Struct | 0.1355 ns | 0.0073 ns | 0.0061 ns | - | - | -| ObjectFactory_Struct_Delegate | 1.7573 ns | 0.0323 ns | 0.0302 ns | - | - | +| Object_Activator | 5.5204 ns | 0.1827 ns | 0.2561 ns | 0.0057 | 24 B | +| Object_ObjectFactory | 5.4420 ns | 0.0637 ns | 0.0532 ns | 0.0057 | 24 B | +| Object_ObjectFactory_Delegate | 5.6431 ns | 0.1214 ns | 0.1135 ns | 0.0057 | 24 B | +| --- +| ObjectPrivateCtor_Activator | 91.9347 ns | 1.0674 ns | 0.8913 ns | 0.0057 | 24 B | +| ObjectPrivateCtor_ObjectFactory | 24.5102 ns | 0.0418 ns | 0.0349 ns | 0.0057 | 24 B | +| ObjectPrivateCtor_ObjectFactory_Delegate | 24.0892 ns | 0.2957 ns | 0.2766 ns | 0.0057 | 24 B | +| --- +| ObjectWithParam_Activator | 337.3210 ns | 1.9566 ns | 1.6339 ns | 0.0763 | 320 B | +| ObjectWithParam_ObjectFactory_Delegate | 32.2363 ns | 0.2639 ns | 0.2339 ns | 0.0057 | 24 B | + +#### Value Types + +| Method | Mean | Error | StdDev | Gen0 | Allocated | |----------------------------------------- |------------:|----------:|----------:|-------:|----------:| -| Activator_StructWithParams | 344.7916 ns | 6.7640 ns | 7.5182 ns | 0.0935 | 392 B | -| ObjectFactory_StructWithParams_Delegate | 193.1942 ns | 1.5188 ns | 1.4207 ns | 0.0706 | 296 B | -``` +| Struct_Activator | 0.1423 ns | 0.0129 ns | 0.0121 ns | - | - | +| Struct_ObjectFactory | 0.1334 ns | 0.0060 ns | 0.0056 ns | - | - | +| Struct_ObjectFactory_Delegate | 0.2120 ns | 0.0297 ns | 0.0330 ns | - | - | +| --- +| StructWithParams_Activator | 317.8065 ns | 1.0806 ns | 0.8437 ns | 0.0935 | 392 B | +| StructWithParams_ObjectFactory_Delegate | 48.9843 ns | 0.7990 ns | 0.7474 ns | 0.0191 | 80 B | + +### .NET Framework 4.8 + +#### Reference Types -**.NET Framework** -``` | Method | Mean | Error | StdDev | Gen0 | Allocated | |----------------------------------------- |-------------:|-----------:|-----------:|-------:|----------:| -| Activator_Object | 72.919 ns | 1.5117 ns | 2.8762 ns | 0.0057 | 24 B | -| ObjectFactory_Object | 12.983 ns | 0.2200 ns | 0.1837 ns | 0.0057 | 24 B | -| ObjectFactory_Object_Delegate | 12.500 ns | 0.1472 ns | 0.1229 ns | 0.0057 | 24 B | -|----------------------------------------- |-------------:|-----------:|-----------:|-------:|----------:| -| Activator_ObjectPrivateCtor | 57.444 ns | 1.0517 ns | 1.8420 ns | 0.0057 | 24 B | -| ObjectFactory_ObjectPrivateCtor | 13.601 ns | 0.3442 ns | 0.5152 ns | 0.0057 | 24 B | -| ObjectFactory_ObjectPrivateCtor_Delegate | 12.489 ns | 0.1248 ns | 0.0975 ns | 0.0057 | 24 B | -|----------------------------------------- |-------------:|-----------:|-----------:|-------:|----------:| -| Activator_ObjectWithParam | 966.560 ns | 9.5869 ns | 8.0055 ns | 0.1030 | 433 B | -| ObjectFactory_ObjectWithParam_Delegate | 11.007 ns | 0.1479 ns | 0.1311 ns | 0.0057 | 24 B | -|----------------------------------------- |-------------:|-----------:|-----------:|-------:|----------:| -| Activator_Struct | 54.208 ns | 0.6618 ns | 0.5867 ns | 0.0076 | 32 B | -| ObjectFactory_Struct | 4.110 ns | 0.1152 ns | 0.1233 ns | - | - | -| ObjectFactory_Struct_Delegate | 3.207 ns | 0.0541 ns | 0.0506 ns | - | - | +| Object_Activator | 64.296 ns | 0.5001 ns | 0.4176 ns | 0.0057 | 24 B | +| Object_ObjectFactory | 17.374 ns | 0.1401 ns | 0.1090 ns | 0.0057 | 24 B | +| Object_ObjectFactory_Delegate | 11.589 ns | 0.2366 ns | 0.2097 ns | 0.0057 | 24 B | +| --- +| ObjectPrivateCtor_Activator | 57.444 ns | 1.0517 ns | 1.8420 ns | 0.0057 | 24 B | +| Object_ObjectFactoryPrivateCtor | 15.704 ns | 0.2986 ns | 0.2499 ns | 0.0057 | 24 B | +| Object_ObjectFactoryPrivateCtor_Delegate | 11.608 ns | 0.2255 ns | 0.2110 ns | 0.0057 | 24 B | +| --- +| ObjectWithParam_Activator | 898.057 ns | 6.3406 ns | 4.9503 ns | 0.1030 | 433 B | +| ObjectWithParam_ObjectFactory_Delegate | 9.940 ns | 0.0301 ns | 0.0267 ns | 0.0057 | 24 B | + +#### Value Types + +| Method | Mean | Error | StdDev | Gen0 | Allocated | |----------------------------------------- |-------------:|-----------:|-----------:|-------:|----------:| -| Activator_StructWithParams | 1,124.746 ns | 15.8511 ns | 13.2364 ns | 0.1202 | 505 B | -| ObjectFactory_StructWithParams_Delegate | 8.977 ns | 0.1744 ns | 0.1362 ns | - | - | -``` +| Struct_Activator | 49.163 ns | 0.3674 ns | 0.3437 ns | 0.0076 | 32 B | +| Struct_ObjectFactory | 3.894 ns | 0.0265 ns | 0.0235 ns | - | - | +| Struct_ObjectFactory_Delegate | 3.069 ns | 0.0138 ns | 0.0123 ns | - | - | +| --- +| StructWithParams_Activator | 1,116.467 ns | 4.2834 ns | 4.0067 ns | 0.1202 | 505 B | +| StructWithParams_ObjectFactory_Delegate | 8.235 ns | 0.0357 ns | 0.0279 ns | - | - | -This is the benchmark code used to generate the above results: +### Benchmark Code ```cs -private readonly DefaultActivator _objectFactory = ObjectFactory.GetActivator(); -private readonly DefaultActivator _objectFactoryPrivateCtor = ObjectFactory.GetActivator(true); -private readonly Func _objectFactoryWithParam = ObjectFactory.GetActivator(); -private readonly DefaultActivator _structFactory = ObjectFactory.GetActivator(); -private readonly Func _structFactoryWithParams = ObjectFactory.GetActivator(); +private readonly DefaultActivator _objectFactory = + ObjectFactory.GetActivator(); +private readonly DefaultActivator _objectFactoryPrivateCtor = + ObjectFactory.GetActivator(true); +private readonly Func _objectFactoryWithParam = + ObjectFactory.GetActivator(); +private readonly DefaultActivator _structFactory = + ObjectFactory.GetActivator(); +private readonly Func _structFactoryWithParams = + ObjectFactory.GetActivator(); [Benchmark] -public SomeObject Activator_Object() +public SomeObject Object_Activator() { return Activator.CreateInstance(); } [Benchmark] -public SomeObject ObjectFactory_Object() +public SomeObject Object_ObjectFactory() { return ObjectFactory.CreateInstance(); } [Benchmark] -public SomeObject ObjectFactory_Object_Delegate() +public SomeObject Object_ObjectFactory_Delegate() { return _objectFactory.Invoke(); } [Benchmark] -public SomeObjectPrivateCtor Activator_ObjectPrivateCtor() +public SomeObjectPrivateCtor ObjectPrivateCtor_Activator() { return (SomeObjectPrivateCtor)Activator.CreateInstance(typeof(SomeObjectPrivateCtor), true)!; } [Benchmark] -public SomeObjectPrivateCtor ObjectFactory_ObjectPrivateCtor() +public SomeObjectPrivateCtor ObjectPrivateCtor_ObjectFactory() { return ObjectFactory.CreateInstance(true); } [Benchmark] -public SomeObjectPrivateCtor ObjectFactory_ObjectPrivateCtor_Delegate() +public SomeObjectPrivateCtor ObjectPrivateCtor_ObjectFactory_Delegate() { return _objectFactoryPrivateCtor.Invoke(); } [Benchmark] -public SomeObject Activator_ObjectWithParam() +public SomeObject ObjectWithParam_Activator() { return (SomeObject)Activator.CreateInstance(typeof(SomeObject), "test")!; } [Benchmark] -public SomeObject ObjectFactory_ObjectWithParam_Delegate() +public SomeObject ObjectWithParam_ObjectFactory_Delegate() { return _objectFactoryWithParam.Invoke("test"); } [Benchmark] -public SomeStruct Activator_Struct() +public SomeStruct Struct_Activator() { return Activator.CreateInstance(); } [Benchmark] -public SomeStruct ObjectFactory_Struct() +public SomeStruct Struct_ObjectFactory() { return ObjectFactory.CreateInstance(); } [Benchmark] -public SomeStruct ObjectFactory_Struct_Delegate() +public SomeStruct Struct_ObjectFactory_Delegate() { return _structFactory.Invoke(); } [Benchmark] -public SomeStruct Activator_StructWithParams() +public SomeStruct StructWithParams_Activator() { return (SomeStruct)Activator.CreateInstance(typeof(SomeStruct), 1L, 2L)!; } [Benchmark] -public SomeStruct ObjectFactory_StructWithParams_Delegate() +public SomeStruct StructWithParams_ObjectFactory_Delegate() { return _structFactoryWithParams.Invoke(1, 2); } diff --git a/Source/Directory.Build.props b/Source/Directory.Build.props index 37db357..c3e9518 100644 --- a/Source/Directory.Build.props +++ b/Source/Directory.Build.props @@ -1,28 +1,28 @@ - - 12.0 - enable - true + + 12.0 + enable + true - + - - true - + + true + - - + + - - - - - + + + + + \ No newline at end of file diff --git a/Source/Singulink.Reflection.ObjectFactory/DefaultActivator.cs b/Source/Singulink.Reflection.ObjectFactory/DefaultActivator.cs index 125ac77..2af0fdf 100644 --- a/Source/Singulink.Reflection.ObjectFactory/DefaultActivator.cs +++ b/Source/Singulink.Reflection.ObjectFactory/DefaultActivator.cs @@ -1,10 +1,14 @@ using System; using System.Diagnostics; +using System.Reflection; using System.Runtime.CompilerServices; namespace Singulink.Reflection; -#pragma warning disable IL2091 // Dynamic T requirements are specified in ObjectFactory and not needed here. +// Dynamic T requirements are specified in ObjectFactory and not needed here. +#pragma warning disable IL2091 +#pragma warning disable IL2087 +#pragma warning disable IL2090 /// /// Creates instances of objects using parameterless constructors. @@ -15,28 +19,32 @@ namespace Singulink.Reflection; /// public readonly struct DefaultActivator { + internal static bool IsPublic { get; } = typeof(T).IsValueType || typeof(T).GetConstructor(Type.EmptyTypes) is not null; + + internal static Func? Delegate { get; private set; } = TryGetDelegate(); + #if !NETSTANDARD // For value types, always use Activator.CreateInstance(). // For referece types: // * If running on JIT: always use delegate. // * If running on AOT: Activator.CreateInstance() is faster than the delegate so use it if there is a public ctor. // The delegate is faster than CreateInstance(Type). - internal static readonly bool UseGenericSystemActivator = typeof(T).IsValueType || - (!RuntimeFeature.IsDynamicCodeCompiled && typeof(T).GetConstructor(Type.EmptyTypes) != null); + internal static readonly bool UseSystemActivator = typeof(T).IsValueType || + (!RuntimeFeature.IsDynamicCodeCompiled && IsPublic); #endif - internal readonly Func? _activator; + private readonly Func? _delegate; /// /// Initializes a new instance of the struct. /// Only call this constructor if the type is a reference type. /// - internal DefaultActivator(Func activator) + internal DefaultActivator(Func activatorDelegate) { #if !NETSTANDARD Debug.Assert(!typeof(T).IsValueType, "Do not call this constructor for value types."); #endif - _activator = activator; + _delegate = activatorDelegate; } /// @@ -44,19 +52,20 @@ internal DefaultActivator(Func activator) /// /// The activator was not properly initialized. /// - /// For performance reasons, calling directly is preferred over using the delegate returned by this method. + /// For performance reasons, calling directly on the default activator is preferred over using the delegate returned by this method. /// public Func AsDelegate() { #if !NETSTANDARD - if (UseGenericSystemActivator) - return static () => Activator.CreateInstance(); + // Duplicate IsValueType check for AOT. Do not remove. + if (typeof(T).IsValueType || UseSystemActivator) + return Delegate ??= static () => Activator.CreateInstance(); #endif - if (_activator == null) + if (_delegate == null) ThrowNotInitialized(); - return _activator!; + return _delegate!; } /// @@ -66,14 +75,43 @@ public Func AsDelegate() public T Invoke() { #if !NETSTANDARD - if (UseGenericSystemActivator) + // Duplicate IsValueType check for AOT. Do not remove. + if (typeof(T).IsValueType || UseSystemActivator) return Activator.CreateInstance(); #endif - if (_activator == null) + if (_delegate == null) ThrowNotInitialized(); - return _activator!.Invoke(); + return _delegate!.Invoke(); + } + + private static Func? TryGetDelegate() + { + try + { +#if !NETSTANDARD + if (typeof(T).IsValueType) + { + return null; + } +#endif +#if NET8_0_OR_GREATER + else if (!RuntimeFeature.IsDynamicCodeCompiled) + { + var constructor = ObjectFactory.GetConstructor(typeof(T), Type.EmptyTypes); + var invoker = ConstructorInvoker.Create(constructor); + + return () => (T)invoker.Invoke(); + } +#endif + return ObjectFactory.GetActivatorByDelegate>(typeof(T), true); + } + catch (Exception ex) + { + Trace.WriteLine($"[ObjectFactory] Failed to create default activator delegate for type {typeof(T).FullName}: {ex}"); + return null; + } } private static void ThrowNotInitialized() => throw new InvalidOperationException("Activator was not properly initialized."); diff --git a/Source/Singulink.Reflection.ObjectFactory/ObjectFactory.cs b/Source/Singulink.Reflection.ObjectFactory/ObjectFactory.cs index dbd31e8..3381569 100644 --- a/Source/Singulink.Reflection.ObjectFactory/ObjectFactory.cs +++ b/Source/Singulink.Reflection.ObjectFactory/ObjectFactory.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -9,6 +10,8 @@ namespace Singulink.Reflection; +#pragma warning disable SA1010 // Opening square brackets should be spaced correctly triggers on collection literals. + /// /// Provides methods to create objects and delegates that create instances based on specified constructor signatures. /// @@ -19,32 +22,15 @@ public static class ObjectFactory private const DynamicallyAccessedMemberTypes PublicConstructors = DynamicallyAccessedMemberTypes.PublicConstructors; private const DynamicallyAccessedMemberTypes DefaultConstructors = DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.NonPublicConstructors; - private static readonly ConcurrentDictionary<(Type ObjectType, Type DelegateType), (Delegate Activator, bool IsPublic)> _delegateActivatorCache = new(); + private record struct Key( + [property: DynamicallyAccessedMembers(AllConstructors)] + [param: DynamicallyAccessedMembers(AllConstructors)] + Type ObjectType, + Type DelegateType); - private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultConstructors)] T> - { - public static readonly Func? Activator = TryGetActivator(); - public static readonly bool IsPublic = typeof(T).IsValueType || typeof(T).GetConstructor(Type.EmptyTypes) is not null; + private record struct DelegateInfo(Delegate Activator, bool IsPublic); - private static Func? TryGetActivator() - { -#if !NETSTANDARD - if (DefaultActivator.UseGenericSystemActivator) - return null; -#endif - - try - { -#pragma warning disable IL2087 // Justification: Only default ctors needed. - return GetActivatorByDelegate>(typeof(T), true); -#pragma warning restore IL2087 - } - catch - { - return null; - } - } - } + private static readonly ConcurrentDictionary s_delegateActivatorCache = new(); /// /// Creates an object of the specified type using the public default constructor. @@ -52,7 +38,7 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T CreateInstance<[DynamicallyAccessedMembers(PublicDefaultConstructor)] T>() { -#pragma warning disable IL2091 // Justification: Only public default ctor needed. +#pragma warning disable IL2091 // Only public default ctor needed. return CreateInstance(false); #pragma warning restore IL2091 } @@ -64,22 +50,20 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo public static T CreateInstance<[DynamicallyAccessedMembers(DefaultConstructors)] T>(bool nonPublic) { #if !NETSTANDARD - if (DefaultActivator.UseGenericSystemActivator) + // Duplicate IsValueType check for AOT. Do not remove. + if (typeof(T).IsValueType || DefaultActivator.UseSystemActivator) return Activator.CreateInstance(); #endif - if (!DefaultActivatorCache.IsPublic && !nonPublic) - ThrowNonPublicConstructorException(typeof(T)); + if (!nonPublic && !DefaultActivator.IsPublic) + ThrowNonPublicCtorException(typeof(T)); - var activator = DefaultActivatorCache.Activator; + var activatorDelegate = DefaultActivator.Delegate; - if (activator == null) - { - Throw(); - static void Throw() => throw new MissingMethodException($"No parameterless constructor defined for type '{typeof(T)}'."); - } + if (activatorDelegate == null) + ThrowNoParameterlessCtor(typeof(T)); - return DefaultActivatorCache.Activator!(); + return activatorDelegate!.Invoke(); } /// @@ -103,7 +87,7 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo /// public static DefaultActivator GetActivator<[DynamicallyAccessedMembers(PublicDefaultConstructor)] T>() { -#pragma warning disable IL2091 // Justification: Only public default ctor needed. +#pragma warning disable IL2091 // Only public default ctor needed. return GetActivator(false); #pragma warning restore IL2091 } @@ -112,7 +96,7 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo /// Gets an activator that creates objects of the specified type using the default constructor, optionally calling a non-public constructor as well. /// /// - /// You can get a delegate by calling on the returned activator. + /// You can get a delegate with the method on the returned activator. /// public static DefaultActivator GetActivator<[DynamicallyAccessedMembers(DefaultConstructors)] T>(bool nonPublic) { @@ -120,10 +104,15 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo if (typeof(T).IsValueType) return default; #endif + var activatorDelegate = DefaultActivator.Delegate; -#pragma warning disable IL2087 // Justification: Only default ctors needed. - return new(GetActivatorByDelegate>(typeof(T), nonPublic)); -#pragma warning restore IL2087 + if (activatorDelegate == null) + ThrowNoParameterlessCtor(typeof(T)); + + if (!nonPublic && !DefaultActivator.IsPublic) + ThrowNonPublicCtorException(typeof(T)); + + return new(activatorDelegate!); } /// @@ -131,7 +120,7 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo /// public static Func GetActivator() { -#pragma warning disable IL2091 // Justification: Only public ctors needed. +#pragma warning disable IL2091 // Only public ctors needed. return GetActivator(false); #pragma warning restore IL2091 } @@ -142,6 +131,17 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo /// public static Func GetActivator(bool nonPublic) { +#if NET8_0_OR_GREATER + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return GetOrCreateActivatorByDelegate>(typeof(T), nonPublic, static key => { + var constructor = GetConstructor(typeof(T), [typeof(TParam)]); + var invoker = ConstructorInvoker.Create(constructor); + return new((TParam p) => (T)invoker.Invoke(p), constructor.IsPublic); + }); + } +#endif + return GetActivatorByDelegate>(typeof(T), nonPublic); } @@ -150,7 +150,7 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo /// public static Func GetActivator() { -#pragma warning disable IL2091 // Justification: Only public ctors needed. +#pragma warning disable IL2091 // Only public ctors needed. return GetActivator(false); #pragma warning restore IL2091 } @@ -161,6 +161,17 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo /// public static Func GetActivator(bool nonPublic) { +#if NET8_0_OR_GREATER + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return GetOrCreateActivatorByDelegate>(typeof(T), false, static key => { + var constructor = GetConstructor(typeof(T), [typeof(TParam1), typeof(TParam2)]); + var invoker = ConstructorInvoker.Create(constructor); + return new((TParam1 p1, TParam2 p2) => (T)invoker.Invoke(p1, p2), constructor.IsPublic); + }); + } +#endif + return GetActivatorByDelegate>(typeof(T), nonPublic); } @@ -169,7 +180,7 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo /// public static Func GetActivator() { -#pragma warning disable IL2091 // Justification: Only public ctors needed. +#pragma warning disable IL2091 // Only public ctors needed. return GetActivator(false); #pragma warning restore IL2091 } @@ -180,6 +191,17 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo /// public static Func GetActivator(bool nonPublic) { +#if NET8_0_OR_GREATER + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return GetOrCreateActivatorByDelegate>(typeof(T), false, static key => { + var constructor = GetConstructor(typeof(T), [typeof(TParam1), typeof(TParam2), typeof(TParam3)]); + var invoker = ConstructorInvoker.Create(constructor); + return new((TParam1 p1, TParam2 p2, TParam3 p3) => (T)invoker.Invoke(p1, p2, p3), constructor.IsPublic); + }); + } +#endif + return GetActivatorByDelegate>(typeof(T), nonPublic); } @@ -188,7 +210,7 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo /// public static Func GetActivator() { -#pragma warning disable IL2091 // Justification: Only public ctors needed. +#pragma warning disable IL2091 // Only public ctors needed. return GetActivator(false); #pragma warning restore IL2091 } @@ -199,6 +221,17 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo /// public static Func GetActivator(bool nonPublic) { +#if NET8_0_OR_GREATER + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return GetOrCreateActivatorByDelegate>(typeof(T), false, static key => { + var constructor = GetConstructor(typeof(T), [typeof(TParam1), typeof(TParam2), typeof(TParam3), typeof(TParam4)]); + var invoker = ConstructorInvoker.Create(constructor); + return new((TParam1 p1, TParam2 p2, TParam3 p3, TParam4 p4) => (T)invoker.Invoke(p1, p2, p3, p4), constructor.IsPublic); + }); + } +#endif + return GetActivatorByDelegate>(typeof(T), nonPublic); } @@ -207,7 +240,7 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo /// public static Func GetActivator() { -#pragma warning disable IL2091 // Justification: Only public ctors needed. +#pragma warning disable IL2091 // Only public ctors needed. return GetActivator(false); #pragma warning restore IL2091 } @@ -218,6 +251,23 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo /// public static Func GetActivator(bool nonPublic) { +#if NET8_0_OR_GREATER + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return GetOrCreateActivatorByDelegate>(typeof(T), false, static key => { + var constructor = GetConstructor(typeof(T), [typeof(TParam1), typeof(TParam2), typeof(TParam3), typeof(TParam4), typeof(TParam5)]); + var invoker = ConstructorInvoker.Create(constructor); + + return new DelegateInfo( + (TParam1 p1, TParam2 p2, TParam3 p3, TParam4 p4, TParam5 p5) => { + Span args = [p1, p2, p3, p4, p5]; + return (T)invoker.Invoke(args); + }, + constructor.IsPublic); + }); + } +#endif + return GetActivatorByDelegate>(typeof(T), nonPublic); } @@ -230,7 +280,7 @@ private static class DefaultActivatorCache<[DynamicallyAccessedMembers(DefaultCo public static TDelegate GetActivatorDelegate([DynamicallyAccessedMembers(PublicConstructors)] Type objectType) where TDelegate : Delegate { -#pragma warning disable IL2067 // Justification: Only public ctors needed. +#pragma warning disable IL2067 // Only public ctors needed. return GetActivatorByDelegate(objectType, false); #pragma warning restore IL2067 } @@ -249,19 +299,31 @@ public static TDelegate GetActivatorDelegate([DynamicallyAccessedMemb public static TDelegate GetActivatorByDelegate([DynamicallyAccessedMembers(AllConstructors)] Type objectType, bool nonPublic) where TDelegate : Delegate { - if (!_delegateActivatorCache.TryGetValue((objectType, typeof(TDelegate)), out var info)) - info = _delegateActivatorCache.GetOrAdd((objectType, typeof(TDelegate)), _ => CreateActivator(objectType, nonPublic)); + return GetOrCreateActivatorByDelegate(objectType, nonPublic, static key => CreateActivator(key.ObjectType, true)); + } + + internal static ConstructorInfo GetConstructor([DynamicallyAccessedMembers(AllConstructors)] Type objectType, Type[] parameterTypes) + { + const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + return objectType.GetConstructor(bindingFlags, null, parameterTypes, null) ?? throw new MissingMethodException("A matching constructor was not found."); + } + + private static TDelegate GetOrCreateActivatorByDelegate([DynamicallyAccessedMembers(AllConstructors)] Type objectType, bool nonPublic, Func infoFactory) + where TDelegate : Delegate + { + var key = new Key(objectType, typeof(TDelegate)); + var info = s_delegateActivatorCache.GetOrAdd(key, infoFactory); if (!nonPublic && !info.IsPublic) - ThrowNonPublicConstructorException(objectType); + ThrowNonPublicCtorException(objectType); return (TDelegate)info.Activator; } - private static (TDelegate Activator, bool IsPublic) CreateActivator([DynamicallyAccessedMembers(AllConstructors)] Type objectType, bool nonPublic) + private static DelegateInfo CreateActivator([DynamicallyAccessedMembers(AllConstructors)] Type objectType, bool nonPublic) where TDelegate : Delegate { -#pragma warning disable IL2090 // Justification: Delegates always generate metadata for the Invoke method. +#pragma warning disable IL2090 // Delegates always generate metadata for the Invoke method. var delegateInfo = typeof(TDelegate).GetMethod("Invoke", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); #pragma warning restore IL2090 @@ -294,15 +356,12 @@ private static (TDelegate Activator, bool IsPublic) CreateActivator([ } else { - const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - - ConstructorInfo constructor = objectType.GetConstructor(bindingFlags, null, parameterTypes, null) - ?? throw new MissingMethodException("A matching constructor was not found."); + var constructor = GetConstructor(objectType, parameterTypes); isPublic = constructor.IsPublic; if (!nonPublic && !isPublic) - ThrowNonPublicConstructorException(objectType); + ThrowNonPublicCtorException(objectType); expression = Expression.New(constructor, parameterExpressions); } @@ -310,8 +369,10 @@ private static (TDelegate Activator, bool IsPublic) CreateActivator([ if (objectType.IsValueType && returnType != objectType) expression = Expression.Convert(expression, returnType); - return (Expression.Lambda(expression, parameterExpressions).Compile(), isPublic); + return new(Expression.Lambda(expression, parameterExpressions).Compile(), isPublic); } - private static void ThrowNonPublicConstructorException(Type objectType) => throw new MissingMethodException($"Requested constructor for type '{objectType}' is not public."); + private static void ThrowNoParameterlessCtor(Type type) => throw new MissingMethodException($"No parameterless constructor defined for type '{type}'."); + + private static void ThrowNonPublicCtorException(Type type) => throw new MissingMethodException($"Requested constructor for type '{type}' is not public."); } \ No newline at end of file diff --git a/Source/Singulink.Reflection.ObjectFactory/Singulink.Reflection.ObjectFactory.csproj b/Source/Singulink.Reflection.ObjectFactory/Singulink.Reflection.ObjectFactory.csproj index d61049d..6b9e49c 100644 --- a/Source/Singulink.Reflection.ObjectFactory/Singulink.Reflection.ObjectFactory.csproj +++ b/Source/Singulink.Reflection.ObjectFactory/Singulink.Reflection.ObjectFactory.csproj @@ -1,41 +1,41 @@  - - netstandard2.0;net6.0;net8.0 - Singulink.Reflection - true - key.snk - MIT - 2.0.0 - Singulink - true - factory, activator, formattable - Provides fast object activation and object factory delegates. - © Singulink. All rights reserved. - Singulink Icon 128x128.png - https://github.com/Singulink/Singulink.Reflection.ObjectFactory - true - + + netstandard2.0;net6.0;net8.0 + Singulink.Reflection + true + key.snk + MIT + 2.0.0 + Singulink + true + factory, activator, formattable + Provides fast object activation and object factory delegates. + © Singulink. All rights reserved. + Singulink Icon 128x128.png + https://github.com/Singulink/Singulink.Reflection.ObjectFactory + true + - - true - true - true - true - + + true + true + true + true + - - - + + + - - - - True - - - + + + + True + + + - - - + + +