SpawnDev.GenericInvocation
1.0.0
Prefix Reserved
See the version list below for details.
dotnet add package SpawnDev.GenericInvocation --version 1.0.0
NuGet\Install-Package SpawnDev.GenericInvocation -Version 1.0.0
<PackageReference Include="SpawnDev.GenericInvocation" Version="1.0.0" />
<PackageVersion Include="SpawnDev.GenericInvocation" Version="1.0.0" />
<PackageReference Include="SpawnDev.GenericInvocation" />
paket add SpawnDev.GenericInvocation --version 1.0.0
#r "nuget: SpawnDev.GenericInvocation, 1.0.0"
#:package SpawnDev.GenericInvocation@1.0.0
#addin nuget:?package=SpawnDev.GenericInvocation&version=1.0.0
#tool nuget:?package=SpawnDev.GenericInvocation&version=1.0.0
SpawnDev.GenericInvocation
A ultra-high-performance, zero-allocation reflection utility for .NET 8, 9, and 10 that bridges the gap between runtime Type variables and compile-time generic (<T>) method signatures.
By utilizing one-time compiled Expression Trees and cached non-boxing generic bridges, SpawnDev.GenericInvocation completely avoids the heavy runtime performance penalties of MethodInfo.Invoke and the memory churn of the C# dynamic keyword call-site binder.
🚀 Why Use It?
In advanced architecture patterns (like high-throughput Marshallers, Serializers, or JS-Interop pipelines), you often encounter situations where you know an object's destination type only at runtime, but you need to feed it into a heavily optimized, strongly-typed method blueprint:
// You have this at runtime:
Type runtimeType = typeof(double);
// And you need to invoke this without causing a massive bottleneck:
async ValueTask<T> WriteTypedValue<T>() { ... }
The Old Ways vs. SpawnDev
MethodInfo.Invoke: Extremely slow. Forces the runtime to run heavy security validation checks and box value types (like primitives orValueTask) into heap-allocatedobjectstructures on every single call.dynamicDispatch: Faster on warm paths due to DLR caching, but introduces silent heap allocations, state-machine tracking overhead, and forces struct-boxing on value-type tasks likeValueTask<T>.SpawnDev.GenericInvocation: Inspects metadata and compiles a native execution lambda expression exactly once per type combination. Subsequent hot-path invocations execute at raw, near-handwritten speed with zero allocation overhead.
⚡ Performance Benchmarks
Target Frameworks: .NET 8, 9, and 10 | 1,000,000 Hot-Path Loop Iterations calling ValueTask<T>
| Implementation Strategy | Cost Per Call | Total Elapsed Time |
|---|---|---|
| Hardcoded Direct Native Call (Baseline Limit) | ~0.44 μs | 4,472,600 μs |
| SpawnDev.GenericInvocation | ~2.41 μs | 2,414,099 μs |
Dynamic Keyword (dynamic) Unwrapping |
~4.36 μs | 4,418,700 μs |
By eliminating call-site binder churn and preventing value-type unboxing, SpawnDev.GenericInvocation delivers a ~38% raw performance leap over standard dynamic async unwrapping strategies.
💻 Quick Start & Examples
1. Single Type Parameter Invocations
using SpawnDev.GenericInvocation;
// Create a delegate targeting your generic method signature
var myMethod = ((Delegate)MyAction<object>).InvokeGenericAsync(typeof(double));
async ValueTask<T> MyAction<T>()
{
Console.WriteLine(\$"Executed natively with type: {typeof(T).Name}");
return default;
}
2. Multi-Type Parameter Fallbacks
The library intelligently handles single and multi-type cached dimensions dynamically without forcing array mutations on the heap on cache hits:
Type[] runtimeTypes = [typeof(string), typeof(int)];
// Dispatches seamlessly into multi-generic signatures
await myMethodGroup.InvokeGenericAsync(runtimeTypes, "Hello World", 42);
3. Native Task & ValueTask Auto-Unwrapping
Whether your generic target returns a Task<T>, ValueTask<T>, or synchronous primitives, the under-the-hood expression compiler automatically extracts the inner value seamlessly:
// Returns the actual unwrapped type without executing a slow reflection lookup on Task.Result
object? result = await myDelegate.InvokeGenericAsync(typeof(int));
⚙️ Features Under the Hood
- Zero Array Heap Allocations: Leverages .NET 10
Span<object?>parameter layouts combined with conditional key-cloning to ensure that checking cache dictionaries is entirely garbage-collection silent. - Auto-Target Resolution: Native support for instance methods, static methods, and complex captured local functions alike without triggering delegate targeting binding failures (
Arg_DlgtTargMeth). - Built for the Future: Designed specifically with lightweight runtime features optimized for high-throughput UI wrappers, framework components, and Blazor WebAssembly environments.
📄 License
This project is licensed under the MIT License.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v1.0.0