Pico.DI
2026.1.10
dotnet add package Pico.DI --version 2026.1.10
NuGet\Install-Package Pico.DI -Version 2026.1.10
<PackageReference Include="Pico.DI" Version="2026.1.10" />
<PackageVersion Include="Pico.DI" Version="2026.1.10" />
<PackageReference Include="Pico.DI" />
paket add Pico.DI --version 2026.1.10
#r "nuget: Pico.DI, 2026.1.10"
#:package Pico.DI@2026.1.10
#addin nuget:?package=Pico.DI&version=2026.1.10
#tool nuget:?package=Pico.DI&version=2026.1.10
๐ Pico.DI
Zero-reflection, AOT-native dependency injection for .NET โ because your IoC container shouldn't be the bottleneck.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Pico.DI: Compile-time DI that doesn't suck โ
โ โ Zero reflection at runtime โ
โ โ Native AOT compatible โ
โ โ Up to 5x faster than Microsoft.DI โ
โ โ Zero GC allocations on hot paths โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ฏ Why Pico.DI?
| Feature | Pico.DI | Microsoft.DI |
|---|---|---|
| Reflection | โ None | โ Heavy |
| AOT Support | โ Native | โ ๏ธ Limited |
| Factory Generation | Compile-time | Runtime |
| Circular Dependency Detection | Compile-time error | Runtime crash |
| GC Pressure | Zero on resolution | Allocations |
The Problem with Traditional DI
// Microsoft.DI at runtime:
// 1. Reflection to find constructors
// 2. Expression tree compilation
// 3. Dynamic delegate generation
// 4. Runtime type checking
// Result: ~700ns per transient resolution ๐ด
The Pico.DI Solution
// Pico.DI at compile-time:
// Source Generator scans your code โ Generates static factories
// Result: ~16-45ns per resolution ๐ (that's 3-5x faster)
โก Quick Start
Installation
dotnet add package Pico.DI
๐ฆ Just install
Pico.DIโ it pulls in the source generator and abstractions automatically.
Package Architecture
Pico.DI ships as three NuGet packages with a clean dependency chain:
Pico.DI โ Pico.DI.Gen โ Pico.DI.Abs
(runtime) (source gen) (interfaces)
| Package | Install whenโฆ | What you get |
|---|---|---|
| Pico.DI | You're building an application | Runtime container + source generator + abstractions (all transitive) |
| Pico.DI.Gen | You're building a library/extension that needs compile-time code generation | Source generator + abstractions (no runtime container) |
| Pico.DI.Abs | You're building a library/extension that only needs the interfaces | Interfaces and base types only |
Basic Usage
using Pico.DI;
using Pico.DI.Abs;
// Define your services
public interface IGreeter { string Greet(string name); }
public class Greeter : IGreeter
{
public string Greet(string name) => $"Hello, {name}!";
}
// Register services - Source Generator does the heavy lifting
var container = new SvcContainer();
container.RegisterSingleton<IGreeter, Greeter>();
container.Build();
// Resolve
using var scope = container.CreateScope();
var greeter = scope.GetService<IGreeter>();
Console.WriteLine(greeter.Greet("World")); // Hello, World!
Registration Methods
var container = new SvcContainer();
// Lifetime options
container.RegisterTransient<IService, ServiceImpl>(); // New instance every time
container.RegisterScoped<IService, ServiceImpl>(); // One per scope
container.RegisterSingleton<IService, ServiceImpl>(); // One for app lifetime
// Factory registration (explicit control)
container.RegisterSingleton<IService>(sp => new ServiceImpl(
sp.GetService<IDependency>()
));
// Instance registration
container.RegisterSingle<IConfig>(new AppConfig());
// Open generics
container.RegisterTransient(typeof(IRepository<>), typeof(Repository<>));
// Batch registration
container.RegisterRange(descriptors);
container.Build(); // Freeze & optimize
Dependency Injection
public class OrderService(
IOrderRepository repo, // โ Injected
ILogger logger, // โ Injected
IPaymentGateway gateway) // โ Injected
{
public void ProcessOrder(Order order) { /* ... */ }
}
// Source Generator automatically detects constructor parameters
// and generates: sp => new OrderService(
// sp.GetService<IOrderRepository>(),
// sp.GetService<ILogger>(),
// sp.GetService<IPaymentGateway>())
๐ Benchmarks
Environment: .NET 10.0 | Windows | x64 | Native AOT | 100 samples ร 10,000 iterations
Overall Summary
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Pico.DI wins: 18 / 18 scenarios โ
โ Average speedup: 3.04x faster โ
โ GC allocations: ZERO โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
By Service Complexity
NoDependency (Simple service)
| Library | Lifetime | Avg (ns) | P50 (ns) | P90 (ns) | P95 (ns) | P99 (ns) | CPU Cycle | Speedup |
|---|---|---|---|---|---|---|---|---|
| Pico.DI | Transient | 11.7 | 11.6 | 11.9 | 12.0 | 13.7 | 29 | 4.57x ๐ฅ |
| MS.DI | Transient | 53.7 | 51.7 | 60.0 | 65.2 | 73.6 | 130 | baseline |
| Pico.DI | Scoped | 24.3 | 23.8 | 24.9 | 26.9 | 31.8 | 60 | 3.10x |
| MS.DI | Scoped | 75.3 | 73.5 | 74.3 | 76.2 | 81.6 | 184 | baseline |
| Pico.DI | Singleton | 10.0 | 9.5 | 12.1 | 13.3 | 13.5 | 25 | 3.86x |
| MS.DI | Singleton | 38.4 | 36.4 | 46.1 | 51.2 | 52.7 | 96 | baseline |
SingleDependency (Service with 1 dependency)
| Library | Lifetime | Avg (ns) | P50 (ns) | P90 (ns) | P95 (ns) | P99 (ns) | CPU Cycle | Speedup |
|---|---|---|---|---|---|---|---|---|
| Pico.DI | Transient | 31.5 | 31.1 | 32.2 | 32.6 | 34.8 | 79 | 3.24x |
| MS.DI | Transient | 102.3 | 102.0 | 104.9 | 105.7 | 109.2 | 255 | baseline |
| Pico.DI | Scoped | 29.3 | 29.4 | 30.5 | 30.8 | 31.2 | 73 | 2.40x |
| MS.DI | Scoped | 70.3 | 70.0 | 70.5 | 73.5 | 76.5 | 175 | baseline |
| Pico.DI | Singleton | 13.9 | 13.9 | 14.5 | 14.8 | 15.9 | 35 | 2.76x |
| MS.DI | Singleton | 38.4 | 38.2 | 38.8 | 41.6 | 47.7 | 96 | baseline |
MultipleDependencies (Service with 2+ dependencies)
| Library | Lifetime | Avg (ns) | P50 (ns) | P90 (ns) | P95 (ns) | P99 (ns) | CPU Cycle | Speedup |
|---|---|---|---|---|---|---|---|---|
| Pico.DI | Transient | 55.4 | 53.1 | 63.4 | 68.0 | 73.6 | 138 | 2.94x |
| MS.DI | Transient | 162.6 | 162.4 | 167.8 | 168.9 | 171.5 | 405 | baseline |
| Pico.DI | Scoped | 34.8 | 34.9 | 35.5 | 35.8 | 37.3 | 87 | 2.12x |
| MS.DI | Scoped | 73.7 | 73.5 | 74.5 | 79.2 | 80.4 | 184 | baseline |
| Pico.DI | Singleton | 17.6 | 17.8 | 18.0 | 18.4 | 18.6 | 44 | 2.08x |
| MS.DI | Singleton | 36.5 | 36.3 | 36.7 | 36.9 | 38.1 | 91 | baseline |
DeepChain (5-level dependency chain)
| Library | Lifetime | Avg (ns) | P50 (ns) | P90 (ns) | P95 (ns) | P99 (ns) | CPU Cycle | Speedup |
|---|---|---|---|---|---|---|---|---|
| Pico.DI | Transient | 89.4 | 88.6 | 91.1 | 92.4 | 97.9 | 223 | 2.83x |
| MS.DI | Transient | 252.6 | 252.3 | 260.5 | 261.4 | 279.6 | 629 | baseline |
| Pico.DI | Scoped | 27.3 | 27.1 | 28.0 | 28.3 | 29.1 | 68 | 2.43x |
| MS.DI | Scoped | 66.4 | 65.6 | 67.5 | 68.4 | 77.3 | 165 | baseline |
| Pico.DI | Singleton | 13.0 | 12.7 | 14.9 | 15.3 | 16.2 | 32 | 2.68x |
| MS.DI | Singleton | 34.9 | 34.3 | 35.4 | 36.1 | 39.9 | 87 | baseline |
Summary by Lifetime
| Lifetime | Average Speedup |
|---|---|
| Transient | 3.39x faster ๐ฅ |
| Scoped | 2.51x faster |
| Singleton | 2.84x faster |
Summary by Service Complexity
| Complexity | Average Speedup |
|---|---|
| NoDependency | 3.84x faster |
| SingleDependency | 2.80x faster |
| MultipleDependencies | 2.38x faster |
| DeepChain | 2.65x faster |
๐ก Why so fast? Pico.DI generates inlined factory chains at compile-time. No reflection, no expression trees, no runtime codegen. Just pure, static method calls with
[MethodImpl(AggressiveInlining)].
๐ง How It Works
Compile-Time Magic
Your Code Source Generator Output
โโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
container.RegisterSingleton [ModuleInitializer]
<IFoo, Foo>(); static void AutoRegister() {
SvcContainerAutoConfiguration
container.RegisterScoped .RegisterConfigurator(Configure);
<IBar, Bar>(); }
scope.GetService<IFoo>(); static void Configure(ISvcContainer c) {
c.Register(new SvcDescriptor(
typeof(IFoo),
static sp => new Foo(), // โ Compiled!
SvcLifetime.Singleton));
c.Register(new SvcDescriptor(
typeof(IBar),
static sp => new Bar(),
SvcLifetime.Scoped));
}
Resolution Flow
GetService<IFoo>()
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโ
โ FrozenDictionary โ O(1) lookup
โ TryGetValue(typeof) โ
โโโโโโโโโโโโฌโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโ
โ SvcDescriptor โ
โ โโ Factory: sp=>... โ Pre-compiled static lambda
โ โโ Lifetime: ... โ
โ โโ SingleInstance โ For singletons
โโโโโโโโโโโโฌโโโโโโโโโโโโ
โ
โผ
Return instance
๐ง Advanced Usage
Open Generics
// Registration
container.RegisterTransient(typeof(IRepository<>), typeof(Repository<>));
// Usage - Source Generator detects all closed types
var userRepo = scope.GetService<IRepository<User>>(); // โ Generated
var orderRepo = scope.GetService<IRepository<Order>>(); // โ Generated
Circular Dependency Detection
// This won't compile - Source Generator catches it!
public class A(B b) { }
public class B(A a) { } // Error PICO010: Circular dependency detected: A -> B -> A
Testing
[Test]
public async Task OrderService_Should_ProcessOrder()
{
// Skip auto-registration, use mocks
await using var container = new SvcContainer(autoConfigureFromGenerator: false);
container.RegisterSingleton<IOrderRepository>(sp => new MockOrderRepo());
container.RegisterSingleton<IOrderService, OrderService>();
container.Build();
using var scope = container.CreateScope();
var service = scope.GetService<IOrderService>();
// Assert...
}
Multiple Implementations
container.RegisterTransient<IPlugin, PluginA>();
container.RegisterTransient<IPlugin, PluginB>();
container.RegisterTransient<IPlugin, PluginC>();
// Get all implementations
var plugins = scope.GetServices<IPlugin>(); // [PluginA, PluginB, PluginC]
// Get last registered (override pattern)
var plugin = scope.GetService<IPlugin>(); // PluginC
๐ฆ Packages
| Package | Target Audience | Dependencies |
|---|---|---|
Pico.DI |
Application developers | โ Pico.DI.Gen โ Pico.DI.Abs |
Pico.DI.Gen |
Library / extension authors needing code generation | โ Pico.DI.Abs |
Pico.DI.Abs |
Library / extension authors needing only interfaces | None |
For most users: just install Pico.DI โ it transitively brings in the source generator (Pico.DI.Gen) and abstractions (Pico.DI.Abs).
For library authors: reference Pico.DI.Gen if you need compile-time source generation, or Pico.DI.Abs if you only need the DI interfaces. This avoids pulling the runtime container into your library's dependency tree.
๐ฎ Requirements
- .NET 10.0+ (uses C# 14 extension types)
- Roslyn 4.x+ (for source generation)
โ ๏ธ Important: Understanding autoConfigureFromGenerator
The Two Registration Modes
// Mode 1: Auto-configuration (default)
var container = new SvcContainer(); // autoConfigureFromGenerator = true
// Mode 2: Manual configuration
var container = new SvcContainer(autoConfigureFromGenerator: false);
How Registration Actually Works
Placeholder methods (depend on Source Generator):
container.RegisterTransient<IFoo, Foo>(); // โ ๏ธ This is a PLACEHOLDER!
container.RegisterScoped<IBar, Bar>(); // โ ๏ธ Actually does nothing at runtime
container.RegisterSingleton<IBaz, Baz>(); // โ ๏ธ Source Generator intercepts these
Factory methods (real runtime registration):
container.RegisterTransient<IFoo>(sp => new Foo()); // โ
Real registration
container.RegisterScoped<IBar>(sp => new Bar()); // โ
Works without Source Generator
container.RegisterSingleton<IBaz>(sp => new Baz()); // โ
Always works
container.RegisterSingle<IConfig>(new Config()); // โ
Instance registration
The Limitation
autoConfigureFromGenerator |
Placeholder Methods | Factory Methods |
|---|---|---|
true (default) |
โ Work (via Source Generator) | โ Work |
false |
โ Do nothing! | โ Work |
When to Use false
// โ
Unit testing with mocks - use factory methods only
await using var container = new SvcContainer(autoConfigureFromGenerator: false);
container.RegisterSingleton<IRepo>(sp => new MockRepo()); // Factory method
container.RegisterSingleton<IService, MyService>(); // โ Won't work!
container.Build();
// โ
Integration testing - apply generated config then override
await using var container = new SvcContainer(autoConfigureFromGenerator: false);
container.ConfigureGeneratedServices(); // Manually apply
container.RegisterSingleton<IExternal>(sp => new Mock()); // Override specific services
TL;DR
๐จ If
autoConfigureFromGenerator = false, you MUST use factory methods for registration. Placeholder methods likeRegisterTransient<IFoo, Foo>()will silently do nothing.
๐ค Contributing
PRs welcome! Please ensure:
- All tests pass (
dotnet test) - Benchmarks don't regress
- AOT compatibility maintained
๐ License
MIT License - See LICENSE
<p align="center"> <b>Pico.DI</b> โ Dependency Injection at the Speed of Light โก </p>
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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
- Pico.DI.Gen (>= 2026.1.10)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 2026.1.10 | 166 | 3/21/2026 | |
| 2026.1.9 | 129 | 3/13/2026 | |
| 2026.1.8 | 113 | 3/13/2026 | |
| 2026.1.7 | 125 | 3/13/2026 | |
| 2026.1.6 | 125 | 3/9/2026 | |
| 2026.1.3 | 118 | 3/4/2026 | |
| 0.1.2 | 133 | 1/13/2026 | |
| 0.1.0 | 134 | 1/12/2026 | |
| 0.0.8 | 133 | 1/10/2026 | |
| 0.0.5 | 133 | 1/10/2026 | |
| 0.0.4 | 135 | 1/10/2026 | |
| 0.0.3 | 133 | 1/10/2026 | |
| 0.0.2 | 131 | 1/9/2026 | |
| 0.0.1 | 134 | 1/8/2026 |