Pure.DI is a compile-time dependency injection (DI) code generator. Supports .NET starting with .NET Framework 2.0, released 2005-10-27, and all newer versions.
- .NET SDK 6.0.4+
Required for compilation. Projects can target older frameworks (e.g., .NET Framework 2.0). - C# 8+
Only required for projects using the Pure.DI source generator. Other projects support any C# version.
Pure.DI is a .NET code generator designed to produce clean, efficient dependency injection logic. By leveraging basic language constructs, it generates straightforward code indistinguishable from manual implementationβessentially composing objects through nested constructor invocations. Unlike traditional DI frameworks, Pure.DI avoids reflection and dynamic instantiation entirely, eliminating performance penalties associated with runtime overhead.
All analysis of object, constructor, and method graphs occurs at compile time. Pure.DI proactively detects and alerts developers to issues such as missing dependencies, cyclic references, or dependencies unsuitable for injectionβensuring these errors are resolved before execution. This approach guarantees that developers cannot produce a program vulnerable to runtime crashes caused by faulty dependency wiring. The validation process operates seamlessly alongside code development, creating an immediate feedback loop: as you modify your code, Pure.DI verifies its integrity in real time, effectively delivering tested, production-ready logic the moment changes are implemented.
The pure dependency injection approach introduces no runtime dependencies and avoids .NET reflection , ensuring consistent execution across all supported platforms. This includes the Full .NET Framework 2.0+, .NET Core, .NET 5+, UWP/Xbox, .NET IoT, Unity, Xamarin, Native AOT, and beyond. By decoupling runtime constraints, it preserves predictable behavior regardless of the target environment.
The Pure.DI API is intentionally designed to closely mirror the APIs of mainstream IoC/DI frameworks. This approach ensures developers can leverage their existing knowledge of dependency injection patterns without requiring significant adaptation to a proprietary syntax.
Pure.DI recommends utilizing dedicated marker types rather than relying on open generics. This approach enables more precise construction of object graphs while allowing developers to fully leverage the capabilities of generic types.
Pure.DI allows to view and debug the generated code, making debugging and testing easier.
Pure.DI provides native support for numerous Base Class Library (BCL) types out of the box without any extra effort.
Pure.DI is designed for high-performance applications where speed and minimal memory consumption are critical.
Pure.DI is suitable for projects where code cleanliness and minimalism are important factors.
Pure.DI can handle complex dependencies and provides flexible configuration options.
Its high performance, zero memory consumption/preparation overhead, and lack of dependencies make it ideal for building libraries and frameworks.
| NuGet package | Description |
|---|---|
| Pure.DI | DI source code generator |
| Pure.DI.Abstractions | Abstractions for Pure.DI |
| Pure.DI.Templates | Template package, for creating projects from the shell/command line |
| Pure.DI.MS | Add-ons for Pure.DI to work with Microsoft DI |
interface IBox<out T>
{
T Content { get; }
}
interface ICat
{
State State { get; }
}
enum State { Alive, Dead }record CardboardBox<T>(T Content) : IBox<T>;
class ShroedingersCat(Lazy<State> superposition): ICat
{
// The decoherence of the superposition
// at the time of observation via an irreversible process
public State State => superposition.Value;
}Important
Our abstraction and implementation know nothing about the magic of DI or any frameworks.
Add the Pure.DI package to your project:
Let's bind the abstractions to their implementations and set up the creation of the object graph:
DI.Setup(nameof(Composition))
// Models a random subatomic event that may or may not occur
.Bind().As(Singleton).To<Random>()
// Represents a quantum superposition of two states: Alive or Dead
.Bind().To((Random random) => (State)random.Next(2))
.Bind().To<ShroedingersCat>()
// Cardboard box with any contents
.Bind().To<CardboardBox<TT>>()
// Composition Root
.Root<Program>("Root");Note
In fact, the Bind().As(Singleton).To<Random>() binding is unnecessary since Pure.DI supports many .NET BCL types out of the box, including Random. It was added just for the example of using the Singleton lifetime.
The code above specifies the generation of a partial class named Composition. This name is defined in the DI.Setup(nameof(Composition)) call. The class contains a Root property that returns an object graph with an object of type Program as the root. The type and name of the property are defined by calling Root<Program>("Root"). The generated code looks like this:
partial class Composition
{
private readonly Lock _lock = new Lock();
private Random? _random;
public Program Root
{
get
{
var stateFunc = new Func<State>(() => {
if (_random is null)
lock (_lock)
if (_random is null)
_random = new Random();
return (State)_random.Next(2)
});
return new Program(
new CardboardBox<ICat>(
new ShroedingersCat(
new Lazy<State>(
stateFunc))));
}
}
public T Resolve<T>() { ... }
public T Resolve<T>(object? tag) { ... }
public object Resolve(Type type) { ... }
public object Resolve(Type type, object? tag) { ... }
}Class diagram
classDiagram
ShroedingersCat --|> ICat
CardboardBoxαΈICatα³ --|> IBoxαΈICatα³
CardboardBoxαΈICatα³ --|> IEquatableαΈCardboardBoxαΈICatα³α³
Composition ..> Program : Program Root
State o-- "Singleton" Random : Random
ShroedingersCat *-- LazyαΈStateα³ : LazyαΈStateα³
Program *-- CardboardBoxαΈICatα³ : IBoxαΈICatα³
CardboardBoxαΈICatα³ *-- ShroedingersCat : ICat
LazyαΈStateα³ o-- "PerBlock" FuncαΈStateα³ : FuncαΈStateα³
FuncαΈStateα³ *-- State : State
namespace Sample {
class CardboardBoxαΈICatα³ {
<<record>>
+CardboardBox(ICat Content)
}
class Composition {
<<partial>>
+Program Root
+ T ResolveαΈTα³()
+ T ResolveαΈTα³(object? tag)
+ object Resolve(Type type)
+ object Resolve(Type type, object? tag)
}
class IBoxαΈICatα³ {
<<interface>>
}
class ICat {
<<interface>>
}
class Program {
<<class>>
+Program(IBoxαΈICatα³ box)
}
class ShroedingersCat {
<<class>>
+ShroedingersCat(LazyαΈStateα³ superposition)
}
class State {
<<enum>>
}
}
namespace System {
class FuncαΈStateα³ {
<<delegate>>
}
class IEquatableαΈCardboardBoxαΈICatα³α³ {
<<interface>>
}
class LazyαΈStateα³ {
<<class>>
}
class Random {
<<class>>
+Random()
}
}
You can see the class diagram at any time by following the link in the comment of the generated class:

This code does not depend on other libraries, does not use type reflection, and avoids tricks that can negatively affect performance and memory consumption. It looks like efficient code written by hand. At any given time, you can study it and understand how it works.
The public Program Root { get; } property is a Composition Root, the only place in the application where the composition of the object graph takes place. Each instance is created using basic language constructs, which compile with all optimizations and minimal impact on performance and memory consumption. In general, applications may have multiple composition roots and thus such properties. Each composition root must have its own unique name, which is defined when the Root<T>(string name) method is called, as shown in the code above.
class Program(IBox<ICat> box)
{
// Composition Root, a single place in an application
// where the composition of the object graphs
// for an application takes place
static void Main() => new Composition().Root.Run();
private void Run() => Console.WriteLine(box);
}Pure.DI creates efficient code in a pure DI paradigm, using only basic language constructs as if you were writing code by hand. This allows you to take full advantage of Dependency Injection everywhere and always, without any compromise!
The full equivalent of this application with top-level statements can be found here.
Just try creating a project from scratch!
Install the project template
dotnet new install Pure.DI.TemplatesIn a directory, create a console application
dotnet new diRun it
dotnet run- Auto-bindings
- Injections of abstractions
- Composition roots
- Resolve methods
- Simplified binding
- Factory
- Simplified factory
- Injection on demand
- Injections on demand with arguments
- Composition arguments
- Root arguments
- Tags
- Smart tags
- Simplified lifetime-specific bindings
- Simplified lifetime-specific factory
- Build up of an existing object
- Builder
- Builder with arguments
- Builders
- Builders with a name template
- Field injection
- Method injection
- Property injection
- Default values
- Required properties or fields
- Overrides
- Root binding
- Static root
- Async Root
- Consumer type
- Ref dependencies
- Roots
- Roots with filter
- Transient
- Singleton
- PerResolve
- PerBlock
- Scope
- Auto scoped
- Default lifetime
- Default lifetime for a type
- Default lifetime for a type and a tag
- Disposable singleton
- Async disposable singleton
- Async disposable scope
- Func
- Enumerable
- Enumerable generics
- Array
- Lazy
- Task
- ValueTask
- Manually started tasks
- Span and ReadOnlySpan
- Tuple
- Weak Reference
- Async Enumerable
- Service collection
- Func with arguments
- Func with tag
- Keyed service provider
- Service provider
- Service provider with scope
- Overriding the BCL binding
- Generics
- Generic composition roots
- Complex generics
- Generic composition roots with constraints
- Generic async composition roots with constraints
- Custom generic argument
- Build up of an existing generic object
- Generic root arguments
- Complex generic root arguments
- Generic builder
- Generic builders
- Generic roots
- Generic injections on demand
- Generic injections on demand with arguments
- Constructor ordinal attribute
- Dependency attribute
- Member ordinal attribute
- Tag attribute
- Type attribute
- Inject attribute
- Custom attributes
- Custom universal attribute
- Custom generic argument attribute
- Bind attribute
- Bind attribute with lifetime and tag
- Bind attribute for a generic type
- Resolve hint
- ThreadSafe hint
- OnDependencyInjection regular expression hint
- OnDependencyInjection wildcard hint
- OnCannotResolve regular expression hint
- OnCannotResolve wildcard hint
- OnNewInstance regular expression hint
- OnNewInstance wildcard hint
- ToString hint
- Check for a root
- Composition root kinds
- Factory with thread synchronization
- Root with name template
- Tag Any
- Tag Type
- Tag Unique
- Tag on injection site
- Tag on a constructor argument
- Tag on a member
- Tag on a method argument
- Tag on injection site with wildcards
- Dependent compositions
- Inheritance of compositions
- Accumulators
- Global compositions
- Partial class
- A few partial classes
- Thread-safe overrides
- Consumer types
- Tracking disposable instances per a composition root
- Tracking disposable instances in delegates
- Tracking disposable instances using pre-built classes
- Tracking disposable instances with different lifetimes
- Tracking async disposable instances per a composition root
- Tracking async disposable instances in delegates
- Exposed roots
- Exposed roots with tags
- Exposed roots via arg
- Exposed roots via root arg
- Exposed generic roots
- Exposed generic roots with args
- Console
- Unity
- UI
- Web
- GitHub repos with examples
Each generated class (a composition) is configured via Setup(string compositionTypeName):
DI.Setup("Composition")
.Bind<IDependency>().To<Dependency>()
.Bind<IService>().To<Service>()
.Root<IService>("Root");The following class will be generated
partial class Composition
{
// Composition root
public IService Root
{
get
{
return new Service(new Dependency());
}
}
}The compositionTypeName parameter can be omitted:
- If the setup is performed inside a partial class, the composition will be created for that partial class.
- For a class with composition kind
CompositionKind.Global, see this example.
Setup arguments
The first parameter is used to specify the name of the composition class. All sets with the same name will be combined to create one composition class. Alternatively, this name may contain a namespace, e.g. a composition class is generated for Sample.Composition:
namespace Sample
{
partial class Composition
{
...
}
}The second optional parameter may have multiple values to determine the kind of composition.
This value is used by default. If this value is specified, a normal composition class will be created.
If you specify this value, the class will not be generated, but this setup can be used by others as a base setup. For example:
DI.Setup("BaseComposition", CompositionKind.Internal)
.Bind().To<Dependency>();
DI.Setup("Composition").DependsOn("BaseComposition")
.Bind().To<Service>(); If the CompositionKind.Public flag is set in the composition setup, it can also serve as the base for other compositions, as in the example above.
No composition class will be created when this value is specified, but this setup is the base setup for all setups in the current project, and DependsOn(...) is not required.
Constructors
By default, starting with version 2.3.0, no constructors are generated for a composition. The actual set of constructors depends on the composition arguments and lifetime scopes.
If the composition has any arguments defined, Pure.DI automatically generates a public parameterized constructor that includes all specified arguments.
Example configuration:
DI.Setup("Composition")
.Arg<string>("name")
.Arg<int>("id")
// ...Resulting constructor:
public Composition(string name, int id) { /* ... */ }Important notes:
- Only arguments that are actually used in the object graph appear in the constructor.
- Unused arguments are omitted to optimize resource usage.
- If no arguments are specified, no parameterized constructor is created.
If there is at least one binding with Lifetime.Scoped, Pure.DI generates two constructors:
- Public default constructor
Used for creating the root scope instance.
public Composition() { /* ... */ }
- Internal constructor with parent scope
Used for creating child scope instances. This constructor is internal and accepts a single parameter β the parent scope.
internal Composition(Composition parentScope) { /* ... */ }Important notes:
- The public default constructor enables initialization of the root composition.
- The internal constructor with parent reference enables proper scoping hierarchy for
Lifetime.Scopeddependencies.- These constructors are only generated when
Lifetime.Scopedbindings exist in the composition.
- No arguments + no Scoped lifetimes: no constructors generated.
- Arguments present: public parameterized constructor with all used arguments.
- At least one Scoped lifetime: two constructors (public default + internal with parent).
- Both arguments and Scoped lifetimes: all three constructors (parameterized, public default, internal with parent).
Composition Roots
To create an object graph quickly and conveniently, a set of properties (or methods) is formed. These properties/methods are here called composition roots. The type of a property/method is the type of the root object created by the composition. Accordingly, each invocation of a property/method leads to the creation of an object graph with a root element of this type.
DI.Setup("Composition")
.Bind<IService>().To<Service>()
.Root<IService>("MyService");
var composition = new Composition();
var service = composition.MyService;
service = composition.Resolve<IService>();
service = composition.Resolve(typeof(IService));In this case, the property for the IService type will be named MyService and will be available for direct use. The result of its use will be the creation of an object graph with the root of IService type:
public IService MyService
{
get
{
...
return new Service(...);
}
}This is the recommended way to create a composition root. A composition class can contain any number of roots.
In addition, the composition roots can be resolved using the Resolve/ResolveByTag methods:
service = composition.Resolve<IService>();
service = composition.Resolve(typeof(IService));[!TIP]
- There is no limit to the number of roots, but you should consider limiting the number of roots. Ideally, an application should have a single composition root.
- The name of the composition root is arbitrarily chosen depending on its purpose, but should follow C# property naming conventions since it becomes a property name in the composition class.
- It is recommended that composition roots be resolved using normal properties or methods instead of
Resolve/ResolveByTagmethods.
If the root name is empty, an anonymous composition root with a random name is created:
private IService RootM07D16di_0001
{
get { ... }
}These properties (or methods) have an arbitrary name and access modifier private and cannot be used directly from the code. Do not attempt to use them, as their names can change between builds. Anonymous composition roots can be resolved by Resolve/ResolveByTag methods:
DI.Setup("Composition")
.Bind<IService>().To<Service>()
.Root<IService>();
var composition = new Composition();
var service = composition.Resolve<IService>();
service = composition.Resolve(typeof(IService));Resolve/ResolveByTag methods
By default, a set of four Resolve/ResolveByTag methods is generated:
public T Resolve<T>() { ... }
public T Resolve<T>(object? tag) { ... }
public object Resolve(Type type) { ... }
public object Resolve(Type type, object? tag) { ... }These methods can resolve both public and anonymous composition roots that do not depend on root arguments. They are useful when using the Service Locator approach, where the code resolves composition roots in place:
var composition = new Composition();
composition.Resolve<IService>();This is not recommended because Resolve/ResolveByTag methods have a number of disadvantages:
- They provide access to an unlimited set of dependencies.
- Their use can potentially lead to runtime exceptions, for example, when the corresponding root has not been defined.
- Can be slower because they perform a lookup by type and tag.
To control the generation of these methods, see the Resolve hint.
Provides a mechanism to release unmanaged resources. These methods are generated only if the composition contains at least one singleton/scoped instance that implements either IDisposable or IAsyncDisposable. The Dispose() or DisposeAsync() method of the composition should be called to dispose of all created singleton/scoped objects:
using var composition = new Composition();or
await using var composition = new Composition();To dispose objects of other lifetimes, see this or this example.
Bindings
Bindings are the core mechanism of Pure.DI, used to define how types are created and which contracts they fulfill.
To bind a contract to a specific implementation:
.Bind<Contract1>(tags).Bind<ContractN>(tags)
.Tags(tags)
.As(Lifetime)
.To<Implementation>()Alternatively, you can bind multiple contracts at once:
.Bind<Contract1, Contract2>(tags)
.To<Implementation>()Example:
.Bind<IService>().To<Service>()To use a custom factory logic via IContext:
.Bind<Contract1>(tags).Bind<ContractN>(tags)
.Tags(tags)
.As(Lifetime)
.To(ctx => new Implementation(ctx.Resolve<Dependency>()))Example:
.Bind<IService>().To(ctx => new Service(ctx.Resolve<IDependency>()))When you only need to inject specific dependencies without accessing the full context:
.Bind<Contract1>(tags).Bind<ContractN>(tags)
.Tags(tags)
.As(Lifetime)
.To<Implementation>((Dependency1 dep1, Dependency2 dep2) => new Implementation(dep1, dep2))Example:
.Bind<IService>().To((IDependency dep) => new Service(dep))Lifetimes control how long an object lives and how it is reused:
- Transient: A new instance is created for every injection (default).
- Singleton: A single instance is created for the entire composition.
- PerResolve: A single instance is reused within a single composition root (or a
Resolve/ResolveByTagcall). - PerBlock: Reuses instances within a code block to reduce allocations.
- Scoped: A single instance is reused within a specific scope.
You can set a default lifetime for all subsequent bindings in a setup:
.DefaultLifetime(Lifetime.Singleton)
// This will be a Singleton
.Bind<IInterface>().To<Implementation>()Alternatively, you can set a default lifetime for a specific contract type:
.DefaultLifetime<IDisposable>(Lifetime.Singleton)Tags allow you to distinguish between multiple implementations of the same contract.
- Use
.Bind<T>(tags)or.Tags(tags)to apply tags to a binding. - Use the
[Tag(tag)]attribute orctx.Resolve<T>(tag)to consume a tagged dependency.
Example:
.Bind<IService>("MyTag").To<Service>()Implementation bindings allow for a more concise syntax where the implementation type itself serves as the contract or where you want the binder to automatically infer suitable base types and interfaces.
// Infers all suitable base types and interfaces automatically
.Bind(tags).Tags(tags).As(Lifetime).To<Implementation>()Alternatively, you can use the implementation type as the contract:
.Bind().To<Implementation>()Example:
.Bind().To<Service>().Bind(tags).Tags(tags).To(ctx => new Implementation())Example:
.Bind().To(ctx => new Service()).Bind(tags).Tags(tags).To((Dependency dep) => new Implementation(dep))Example:
.Bind().To((IDependency dep) => new Service(dep))By default, Pure.DI avoids binding to special types during auto-inference to prevent polluting the container with unintended bindings for types like IDisposable, IEnumerable, or object. Special types will not be added to bindings by default:
System.ObjectSystem.EnumSystem.MulticastDelegateSystem.DelegateSystem.Collections.IEnumerableSystem.Collections.Generic.IEnumerable<T>System.Collections.Generic.IList<T>System.Collections.Generic.ICollection<T>System.Collections.IEnumeratorSystem.Collections.Generic.IEnumerator<T>System.Collections.Generic.IReadOnlyList<T>System.Collections.Generic.IReadOnlyCollection<T>System.IDisposableSystem.IAsyncResultSystem.AsyncCallback
If you want to add your own special type, use the SpecialType<T>() call, for example:
.SpecialType<MonoBehaviour>()
.Bind().To<MyMonoBehaviourImplementation>()
// Now MonoBehaviour will not be added to the contractsPure.DI provides syntactic sugar for common lifetimes. These methods combine Bind(), .Tags(tags), As(Lifetime), and To() into a single call.
// Equivalent to Bind<T, T1, ...>(tags).As(Lifetime.Transient).To<Implementation>()
.Transient<T>(tags)
// or multiple types at once
.PerResolve<T, T1, ...>(tags)Example:
.Transient<Service>()
.Singleton<Service2, Service3, Service4>()// Equivalent to Bind(tags).As(Lifetime.Singleton).To(ctx => ...)
.Singleton<Implementation>(ctx => new Implementation(), tags)Example:
.Singleton<IService>(ctx => new Service())// Equivalent to Bind(tags).As(Lifetime.PerResolve).To((Dependency dep) => ...)
.PerResolve((Dependency dep) => new Implementation(dep), tags)Example:
.PerResolve((IDependency dep) => new Service(dep))Equivalent shortcuts exist for all lifetimes:
Transient<T>(...)Singleton<T>(...)Scoped<T>(...)PerResolve<T>(...)PerBlock<T>(...)
Setup hints
Hints are used to fine-tune code generation. Setup hints can be used as shown in the following example:
DI.Setup("Composition")
.Hint(Hint.Resolve, "Off")
.Hint(Hint.ThreadSafe, "Off")
.Hint(Hint.ToString, "On")
...In addition, setup hints can be commented out before the Setup method as hint = value. For example:
// Resolve = Off
// ThreadSafe = Off
DI.Setup("Composition")
...Both approaches can be mixed:
// Resolve = Off
DI.Setup("Composition")
.Hint(Hint.ThreadSafe, "Off")
...| Hint | Values | C# version | Default |
|---|---|---|---|
| Resolve | On or Off | On | |
| OnNewInstance | On or Off | 9.0 | Off |
| OnNewInstancePartial | On or Off | On | |
| OnNewInstanceImplementationTypeNameRegularExpression | Regular expression | .+ | |
| OnNewInstanceImplementationTypeNameWildcard | Wildcard | * | |
| OnNewInstanceTagRegularExpression | Regular expression | .+ | |
| OnNewInstanceTagWildcard | Wildcard | * | |
| OnNewInstanceLifetimeRegularExpression | Regular expression | .+ | |
| OnNewInstanceLifetimeWildcard | Wildcard | * | |
| OnDependencyInjection | On or Off | 9.0 | Off |
| OnDependencyInjectionPartial | On or Off | On | |
| OnDependencyInjectionImplementationTypeNameRegularExpression | Regular expression | .+ | |
| OnDependencyInjectionImplementationTypeNameWildcard | Wildcard | * | |
| OnDependencyInjectionContractTypeNameRegularExpression | Regular expression | .+ | |
| OnDependencyInjectionContractTypeNameWildcard | Wildcard | * | |
| OnDependencyInjectionTagRegularExpression | Regular expression | .+ | |
| OnDependencyInjectionTagWildcard | Wildcard | * | |
| OnDependencyInjectionLifetimeRegularExpression | Regular expression | .+ | |
| OnDependencyInjectionLifetimeWildcard | Wildcard | * | |
| OnCannotResolve | On or Off | 9.0 | Off |
| OnCannotResolvePartial | On or Off | On | |
| OnCannotResolveContractTypeNameRegularExpression | Regular expression | .+ | |
| OnCannotResolveContractTypeNameWildcard | Wildcard | * | |
| OnCannotResolveTagRegularExpression | Regular expression | .+ | |
| OnCannotResolveTagWildcard | Wildcard | * | |
| OnCannotResolveLifetimeRegularExpression | Regular expression | .+ | |
| OnCannotResolveLifetimeWildcard | Wildcard | * | |
| OnNewRoot | On or Off | Off | |
| OnNewRootPartial | On or Off | On | |
| ToString | On or Off | Off | |
| ThreadSafe | On or Off | On | |
| ResolveMethodModifiers | Method modifier | public | |
| ResolveMethodName | Method name | Resolve | |
| ResolveByTagMethodModifiers | Method modifier | public | |
| ResolveByTagMethodName | Method name | Resolve | |
| ObjectResolveMethodModifiers | Method modifier | public | |
| ObjectResolveMethodName | Method name | Resolve | |
| ObjectResolveByTagMethodModifiers | Method modifier | public | |
| ObjectResolveByTagMethodName | Method name | Resolve | |
| DisposeMethodModifiers | Method modifier | public | |
| DisposeAsyncMethodModifiers | Method modifier | public | |
| FormatCode | On or Off | Off | |
| SeverityOfNotImplementedContract | Error or Warning or Info or Hidden | Error | |
| Comments | On or Off | On | |
| SkipDefaultConstructor | On or Off | Off | |
| SkipDefaultConstructorImplementationTypeNameRegularExpression | Regular expression | .+ | |
| SkipDefaultConstructorImplementationTypeNameWildcard | Wildcard | * | |
| SkipDefaultConstructorLifetimeRegularExpression | Regular expression | .+ | |
| SkipDefaultConstructorLifetimeWildcard | Wildcard | * | |
| DisableAutoBinding | On or Off | Off | |
| DisableAutoBindingImplementationTypeNameRegularExpression | Regular expression | .+ | |
| DisableAutoBindingImplementationTypeNameWildcard | Wildcard | * | |
| DisableAutoBindingLifetimeRegularExpression | Regular expression | .+ | |
| DisableAutoBindingLifetimeWildcard | Wildcard | * |
The list of hints will be gradually expanded to meet the needs and desires for fine-tuning code generation. Please feel free to add your ideas.
Determines whether to generate Resolve/ResolveByTag methods. By default, a set of four Resolve/ResolveByTag methods are generated. Set this hint to Off to disable the generation of Resolve/ResolveByTag methods. This will reduce the generation time of the class composition, and in this case no anonymous composition roots will be generated. The class composition will be smaller and will only have public roots. When the Resolve hint is disabled, only the public roots properties are available, so be sure to explicitly define them using the Root<T>(string name) method with an explicit composition root name.
Determines whether to use the OnNewInstance partial method. By default, this partial method is not generated. This can be useful, for example, for logging purposes:
internal partial class Composition
{
partial void OnNewInstance<T>(ref T value, object? tag, object lifetime) =>
Console.WriteLine($"'{typeof(T)}'('{tag}') created.");
}You can also replace the created instance with a T type, where T is the actual type of the created instance. To minimize performance loss when calling OnNewInstance, use the three hints below.
Determines whether to generate the OnNewInstance partial method. By default, this partial method is generated when the OnNewInstance hint is On.
This is a regular expression for filtering by instance type name. This hint is useful when OnNewInstance is in On state and it is necessary to limit the set of types for which the OnNewInstance method will be called.
This is a Wildcard for filtering by instance type name. This hint is useful when OnNewInstance is in On state and it is necessary to limit the set of types for which the OnNewInstance method will be called.
This is a regular expression for filtering by tag. This hint is also useful when OnNewInstance is in On state and it is necessary to limit the set of tags for which the OnNewInstance method will be called.
This is a wildcard for filtering by tag. This hint is also useful when OnNewInstance is in On state and it is necessary to limit the set of tags for which the OnNewInstance method will be called.
This is a regular expression for filtering by lifetime. This hint is also useful when OnNewInstance is in On state and it is necessary to restrict the set of lifetimes for which the OnNewInstance method will be called.
This is a wildcard for filtering by lifetime. This hint is also useful when OnNewInstance is in On state and it is necessary to restrict the set of lifetimes for which the OnNewInstance method will be called.
Determines whether to use the OnDependencyInjection partial method when the OnDependencyInjection hint is On to control dependency injection. By default it is Off.
// OnDependencyInjection = On
// OnDependencyInjectionPartial = Off
// OnDependencyInjectionContractTypeNameRegularExpression = ICalculator[\d]{1}
// OnDependencyInjectionTagRegularExpression = Abc
DI.Setup("Composition")
...Determines whether to generate the OnDependencyInjection partial method to control dependency injection. By default, this partial method is not generated. It cannot have an empty body because of the return value. It must be overridden when it is generated. This may be useful, for example, for Interception Scenario.
// OnDependencyInjection = On
// OnDependencyInjectionContractTypeNameRegularExpression = ICalculator[\d]{1}
// OnDependencyInjectionTagRegularExpression = Abc
DI.Setup("Composition")
...To minimize performance loss when calling OnDependencyInjection, use the three tips below.
This is a regular expression for filtering by instance type name. This hint is useful when OnDependencyInjection is in On state and it is necessary to restrict the set of types for which the OnDependencyInjection method will be called.
This is a wildcard for filtering by instance type name. This hint is useful when OnDependencyInjection is in On state and it is necessary to restrict the set of types for which the OnDependencyInjection method will be called.
This is a regular expression for filtering by the name of the resolving type. This hint is also useful when OnDependencyInjection is in On state and it is necessary to limit the set of permissive types for which the OnDependencyInjection method will be called.
This is a wildcard for filtering by the name of the resolving type. This hint is also useful when OnDependencyInjection is in On state and it is necessary to limit the set of permissive types for which the OnDependencyInjection method will be called.
This is a regular expression for filtering by tag. This hint is also useful when OnDependencyInjection is in the On state and you want to limit the set of tags for which the OnDependencyInjection method will be called.
This is a wildcard for filtering by tag. This hint is also useful when OnDependencyInjection is in the On state and you want to limit the set of tags for which the OnDependencyInjection method will be called.
This is a regular expression for filtering by lifetime. This hint is also useful when OnDependencyInjection is in On state and it is necessary to restrict the set of lifetime for which the OnDependencyInjection method will be called.
This is a wildcard for filtering by lifetime. This hint is also useful when OnDependencyInjection is in On state and it is necessary to restrict the set of lifetime for which the OnDependencyInjection method will be called.
Determines whether to use the OnCannotResolve<T>(...) partial method to handle a scenario in which an instance cannot be resolved. By default, this partial method is not generated. Because of the return value, it cannot have an empty body and must be overridden at creation.
// OnCannotResolve = On
// OnCannotResolveContractTypeNameRegularExpression = string|DateTime
// OnDependencyInjectionTagRegularExpression = null
DI.Setup("Composition")
...To avoid missing failed bindings by mistake, use the two relevant hints below.
Determines whether to generate the OnCannotResolve<T>(...) partial method when the OnCannotResolve hint is On to handle a scenario in which an instance cannot be resolved. By default it is On.
// OnCannotResolve = On
// OnCannotResolvePartial = Off
// OnCannotResolveContractTypeNameRegularExpression = string|DateTime
// OnDependencyInjectionTagRegularExpression = null
DI.Setup("Composition")
...To avoid missing failed bindings by mistake, use the two relevant hints below.
Determines whether to use a static partial method OnNewRoot<TContract, T>(...) to handle the new composition root registration event.
// OnNewRoot = On
DI.Setup("Composition")
...Be careful, this hint disables checks for the ability to resolve dependencies!
Determines whether to generate a static partial method OnNewRoot<TContract, T>(...) when the OnNewRoot hint is On to handle the new composition root registration event.
// OnNewRootPartial = Off
DI.Setup("Composition")
...This is a regular expression for filtering by the name of the resolving type. This hint is also useful when OnCannotResolve is in On state and it is necessary to limit the set of resolving types for which the OnCannotResolve method will be called.
This is a wildcard for filtering by the name of the resolving type. This hint is also useful when OnCannotResolve is in On state and it is necessary to limit the set of resolving types for which the OnCannotResolve method will be called.
This is a regular expression for filtering by tag. This hint is also useful when OnCannotResolve is in On state and it is necessary to limit the set of tags for which the OnCannotResolve method will be called.
This is a wildcard for filtering by tag. This hint is also useful when OnCannotResolve is in On state and it is necessary to limit the set of tags for which the OnCannotResolve method will be called.
This is a regular expression for filtering by lifetime. This hint is also useful when OnCannotResolve is in the On state and it is necessary to restrict the set of lifetimes for which the OnCannotResolve method will be called.
This is a wildcard for filtering by lifetime. This hint is also useful when OnCannotResolve is in the On state and it is necessary to restrict the set of lifetimes for which the OnCannotResolve method will be called.
Determines whether to generate the ToString() method. This method provides a class diagram in Mermaid format. To see this diagram, just call the ToString method and copy the text to this site.
// ToString = On
DI.Setup("Composition")
.Bind<IService>().To<Service>()
.Root<IService>("MyService");
var composition = new Composition();
string classDiagram = composition.ToString(); This hint determines whether the object graph will be created in a thread-safe way. The default value of this hint is On. It is a good practice not to use threads when creating an object graph; in this case the hint can be disabled, which will result in a small performance gain. For example:
// ThreadSafe = Off
DI.Setup("Composition")
.Bind<IService>().To<Service>()
.Root<IService>("MyService");Overrides the modifiers of the public T Resolve<T>() method.
Overrides the method name for public T Resolve<T>().
Overrides the modifiers of the public T Resolve<T>(object? tag) method.
Overrides the method name for public T Resolve<T>(object? tag).
Overrides the modifiers of the public object Resolve(Type type) method.
Overrides the method name for public object Resolve(Type type).
Overrides the modifiers of the public object Resolve(Type type, object? tag) method.
Overrides the method name for public object Resolve(Type type, object? tag).
Overrides the modifiers of the public void Dispose() method.
Overrides the modifiers of the public ValueTask DisposeAsync() method.
Specifies whether the generated code should be formatted. This option consumes a lot of CPU resources. This hint may be useful when studying the generated code or, for example, when making presentations.
Indicates the severity level of the situation when, in the binding, an implementation does not implement a contract. Possible values:
- "Error" - this is the default value.
- "Warning" - something suspicious but allowed.
- "Info" - information that does not indicate a problem.
- "Hidden" - not a problem.
Specifies whether the generated code should be commented.
// Represents the composition class
DI.Setup(nameof(Composition))
.Bind<IService>().To<Service>()
// Provides a composition root of my service
.Root<IService>("MyService");Appropriate comments will be added to the generated Composition class and the documentation for the class, depending on the IDE used, will look something like this:
Indicates whether System.Threading.Lock should be used whenever possible instead of the classic approach of synchronizing object access using System.Threading.Monitor. On by default.
DI.Setup(nameof(Composition))
.Hint(Hint.SystemThreadingLock, "Off")
.Bind().To<Service>()
.Root<Service>("MyService");Then documentation for the composition root:
Code generation workflow
flowchart TD
start@{ shape: circle, label: Start }
setups[fa:fa-search DI setups analysis]
types["`fa:fa-search Types analysis
constructors/methods/properties/fields`"]
subgraph dep[Dependency graph]
option[fa:fa-search Selecting a next dependency set]
creating[fa:fa-cog Creating a dependency graph variant]
verification{fa:fa-check-circle Verification}
end
codeGeneration[fa:fa-code Code generation]
compilation[fa:fa-cog Compilation]
failed@{ shape: dbl-circ, label: Compilation failed }
success@{ shape: dbl-circ, label: Success }
start ==> setups
setups -.->|Has problems| failed
setups ==> types
types -.-> |Has problems| failed
types ==> option
option ==> creating
option -.-> |There are no other options| failed
creating ==> verification
verification -->|Has problems| option
verification ==>|Correct| codeGeneration
codeGeneration ==> compilation
compilation -.-> |Has problems| failed
compilation ==> success
Install the DI template Pure.DI.Templates
dotnet new install Pure.DI.TemplatesCreate a "Sample" console application from the di template
dotnet new di -o ./SampleRun it
dotnet run --project SampleFor more information about the template, please see this page.
Version update
When updating the version, it is possible that the previous version of the code generator remains active and is used by compilation services. In this case, the old and new versions of the generator may conflict. For a project where the code generator is used, it is recommended to do the following:
- After updating the version, close the IDE if it is open
- Delete the obj and bin directories
- Run the following commands one by one
dotnet build-server shutdowndotnet restoredotnet buildDisabling API generation
Pure.DI automatically generates its API. If an assembly already has the Pure.DI API, for example, from another assembly, it is sometimes necessary to disable its automatic generation to avoid ambiguity. To do this, you need to add a DefineConstants element to the project files of these modules. For example:
<PropertyGroup>
<DefineConstants>$(DefineConstants);PUREDI_API_SUPPRESSION</DefineConstants>
</PropertyGroup>Display generated files
You can set project properties to save generated files and control their storage location. In the project file, add the <EmitCompilerGeneratedFiles> element to the <PropertyGroup> group and set its value to true. Build the project again. The generated files are now created in the obj/Debug/netX.X/generated/Pure.DI/Pure.DI/Pure.DI.SourceGenerator directory. The path components correspond to the build configuration, the target framework, the source generator project name, and the full name of the generator type. You can choose a more convenient output folder by adding the <CompilerGeneratedFilesOutputPath> element to the application project file. For example:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)Generated</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
</Project>Performance profiling
Please install the JetBrains.dotTrace.GlobalTools dotnet tool globally, for example:
dotnet tool install --global JetBrains.dotTrace.GlobalTools --version 2024.3.3Or make sure it is installed. Add the following sections to the project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PureDIProfilePath>c:\profiling</PureDIProfilePath>
</PropertyGroup>
<ItemGroup>
<CompilerVisibleProperty Include="PureDIProfilePath" />
</ItemGroup>
</Project>Replace a path like c:\profiling with the path where the profiling results will be saved.
Start a build and wait until a file like c:\profiling\pure_di_????.dtt appears in the directory.
Examples of how to set up a composition
Articles
- RU New in Pure.DI by the end of 2024
- RU New in Pure.DI
- RU Pure.DI v2.1
- RU Pure.DI next step
- RU Pure.DI for .NET
RU DotNext video
AI needs to understand the situation itβs in (context). This means knowing details like API, usage scenarios, etc. This helps the AI give more relevant and personalized responses. So Markdown docs below can be useful if you or your team rely on an AI assistant to write code using Pure.DI:
| AI context file | Size | Tokens |
|---|---|---|
| AGENTS_SMALL.md | 30KB | 7K |
| AGENTS_MEDIUM.md | 115KB | 29K |
| AGENTS.md | 329KB | 84K |
For different IDEs, you can use the AGENTS.md file as is by simply copying it to the root directory. For use with JetBrains Rider and Junie, please refer to these instructions. For example, you can copy any AGENTS.md file into your project (using Pure.DI) as .junie/guidelines.md.
Thank you for your interest in contributing to the Pure.DI project! If you are planning a big change or feature, please open an issue first. That way, we can coordinate and understand whether the change fits current priorities and whether we can commit to reviewing and merging it within a reasonable timeframe. We do not want you to spend your valuable time on something that may not align with the direction of Pure.DI.
Contribution prerequisites: .NET SDK 10.0 or later installed.
This repository contains the following directories and files:
π .github GitHub related files and main.yml for building using GitGub actions
π .logs temporary files for generating the README.md file
π .run configuration files for the Rider IDE
π benchmarks projects for performance measurement
π build application for building locally and using CI/CD
π docs resources for the README.md file
π readme sample scripts and examples of application implementations
π samples sample projects
π src source codes of the code generator and all libraries
|- π Pure.DI source code generator project
|- π Pure.DI.Abstractions abstraction library for Pure.DI
|- π Pure.DI.Core basic implementation of the source code generator
|- π Pure.DI.MS project for integration with Microsoft DI
|- π Pure.DI.Templates project templates for creating .NET projects using Pure.DI
|- π Directory.Build.props common MSBUILD properties for all source code generator projects
|- π Library.props common MSBUILD properties for library projects such as Pure.DI.Abstractions
π tests contains projects for testing
|- π Pure.DI.Example project for testing some integration scenarios
|- π Pure.DI.IntegrationTests integration tests
|- π Pure.DI.Tests unit tests for basic functionality
|- π Pure.DI.UsageTests usage tests, used for examples in README.md
|- π Directory.Build.props common MSBUILD properties for all test projects
π LICENSE license file
π build.cmd Windows script file to run one of the build steps, see description below
π build.sh Linux/Mac OS script file to run one of the build steps, see description below
π .space.kts build file using JetBrains space actions
π README.md this README.md file
π SECURITY.md policy file for handling security bugs and vulnerabilities
π Directory.Build.props basic MSBUILD properties for all projects
π Pure.DI.sln .NET solution file
The build logic is a regular .NET console application. You can use build.cmd and build.sh with the appropriate command parameters to perform all basic actions on the project, for example:
| Commands | Description |
|---|---|
| Generate AI context | |
| bm | Run benchmarks |
| c | Compatibility checks |
| dp | Package deployment |
| e | Create examples |
| g | Build and test the source code generator |
| i | Install templates |
| l | Build and test libraries |
| p | Create NuGet packages |
| perf | Performance tests |
| pb | Publish the balazor web sssembly example |
| r | Generate README.md |
| t | Create and deploy templates |
| te | Test examples |
| u | Upgrading the internal version of DI to the latest public version |
For example, to build and test the source code generator:
./build.sh generatoror to run benchmarks:
./build.cmd benchmarksIf you are using the Rider IDE, it already has a set of configurations to run these commands. This project uses C# interactive build automation system for .NET. This tool helps to make .NET builds more efficient.
| Tests | Examples | Performance |
|---|---|---|
Thanks!
BenchmarkDotNet v0.15.8, Windows 10 (10.0.19045.6332/22H2/2022Update) AMD Ryzen 9 5900X 4.20GHz, 1 CPU, 24 logical and 12 physical cores .NET SDK 10.0.100
Transient
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|
| Pure.DI Resolve() | 2.910 ns | 0.1065 ns | 0.0996 ns | 1.00 | 0.04 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Hand Coded | 2.916 ns | 0.0735 ns | 0.0687 ns | 1.00 | 0.03 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Pure.DI composition root | 3.107 ns | 0.0600 ns | 0.0561 ns | 1.07 | 0.03 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Pure.DI Resolve(Type) | 6.366 ns | 0.1763 ns | 0.3179 ns | 2.18 | 0.12 | 0.0014 | 0.0000 | 24 B | 1.00 |
| LightInject | 7.202 ns | 0.1494 ns | 0.1324 ns | 2.47 | 0.07 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Microsoft DI | 8.868 ns | 0.2258 ns | 0.2112 ns | 3.04 | 0.10 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Simple Injector | 10.358 ns | 0.1446 ns | 0.1208 ns | 3.55 | 0.09 | 0.0014 | 0.0000 | 24 B | 1.00 |
| DryIoc | 11.306 ns | 0.1354 ns | 0.1267 ns | 3.88 | 0.10 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Unity | 3,392.103 ns | 28.5980 ns | 25.3514 ns | 1,164.06 | 27.75 | 0.3090 | 0.0000 | 5176 B | 215.67 |
| Autofac | 13,076.143 ns | 56.7879 ns | 50.3410 ns | 4,487.33 | 103.31 | 1.8158 | 0.0916 | 30424 B | 1,267.67 |
| Castle Windsor | 26,189.705 ns | 275.4301 ns | 257.6375 ns | 8,987.49 | 221.40 | 3.0518 | 0.0000 | 51520 B | 2,146.67 |
| Ninject | 132,893.609 ns | 3,993.3789 ns | 11,457.7536 ns | 45,604.97 | 4,047.89 | 6.9580 | 1.3428 | 116872 B | 4,869.67 |
Singleton
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|
| Hand Coded | 3.094 ns | 0.0191 ns | 0.0170 ns | 1.00 | 0.01 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Pure.DI composition root | 3.249 ns | 0.0399 ns | 0.0354 ns | 1.05 | 0.01 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Pure.DI Resolve() | 3.297 ns | 0.1143 ns | 0.1486 ns | 1.07 | 0.05 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Pure.DI Resolve(Type) | 7.764 ns | 0.2040 ns | 0.3978 ns | 2.51 | 0.13 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Microsoft DI | 9.433 ns | 0.0607 ns | 0.0474 ns | 3.05 | 0.02 | 0.0014 | 0.0000 | 24 B | 1.00 |
| DryIoc | 11.085 ns | 0.1281 ns | 0.1199 ns | 3.58 | 0.04 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Simple Injector | 11.684 ns | 0.1633 ns | 0.1448 ns | 3.78 | 0.05 | 0.0014 | 0.0000 | 24 B | 1.00 |
| LightInject | 370.381 ns | 0.9639 ns | 0.8545 ns | 119.72 | 0.69 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Unity | 2,288.160 ns | 33.5301 ns | 31.3641 ns | 739.60 | 10.58 | 0.1869 | 0.0000 | 3184 B | 132.67 |
| Autofac | 8,512.423 ns | 92.7824 ns | 82.2492 ns | 2,751.47 | 29.60 | 1.3123 | 0.0458 | 22048 B | 918.67 |
| Castle Windsor | 13,022.901 ns | 157.0231 ns | 146.8795 ns | 4,209.39 | 51.18 | 1.3733 | 0.0000 | 23112 B | 963.00 |
| Ninject | 63,164.000 ns | 1,228.4213 ns | 3,170.9478 ns | 20,416.50 | 1,024.64 | 3.9063 | 0.9766 | 67040 B | 2,793.33 |
Func
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|
| Pure.DI Resolve() | 3.025 ns | 0.0603 ns | 0.0535 ns | 0.97 | 0.02 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Hand Coded | 3.122 ns | 0.0366 ns | 0.0343 ns | 1.00 | 0.02 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Pure.DI composition root | 3.142 ns | 0.1144 ns | 0.1123 ns | 1.01 | 0.04 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Pure.DI Resolve(Type) | 7.027 ns | 0.1910 ns | 0.4350 ns | 2.25 | 0.14 | 0.0014 | 0.0000 | 24 B | 1.00 |
| DryIoc | 22.635 ns | 0.3097 ns | 0.2897 ns | 7.25 | 0.12 | 0.0072 | 0.0000 | 120 B | 5.00 |
| LightInject | 99.318 ns | 1.6077 ns | 1.4252 ns | 31.82 | 0.56 | 0.0148 | 0.0000 | 248 B | 10.33 |
| Unity | 1,396.159 ns | 27.6920 ns | 25.9031 ns | 447.28 | 9.33 | 0.1507 | 0.0000 | 2552 B | 106.33 |
| Autofac | 4,912.463 ns | 47.3750 ns | 39.5602 ns | 1,573.79 | 20.68 | 0.7782 | 0.0076 | 13128 B | 547.00 |
Enum
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|
| Pure.DI composition root | 8.294 ns | 0.0461 ns | 0.0431 ns | 0.96 | 0.01 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Hand Coded | 8.650 ns | 0.0854 ns | 0.0799 ns | 1.00 | 0.01 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Pure.DI Resolve() | 8.731 ns | 0.1504 ns | 0.1407 ns | 1.01 | 0.02 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Pure.DI Resolve(Type) | 10.197 ns | 0.0790 ns | 0.0739 ns | 1.18 | 0.01 | 0.0014 | 0.0000 | 24 B | 1.00 |
| Microsoft DI | 20.587 ns | 0.4628 ns | 0.8105 ns | 2.38 | 0.09 | 0.0072 | 0.0000 | 120 B | 5.00 |
| LightInject | 54.667 ns | 1.1294 ns | 1.1093 ns | 6.32 | 0.14 | 0.0244 | 0.0000 | 408 B | 17.00 |
| DryIoc | 59.448 ns | 0.7289 ns | 0.6818 ns | 6.87 | 0.10 | 0.0243 | 0.0000 | 408 B | 17.00 |
| Unity | 1,493.509 ns | 18.1381 ns | 15.1462 ns | 172.68 | 2.28 | 0.1278 | 0.0000 | 2168 B | 90.33 |
| Autofac | 12,682.524 ns | 102.3646 ns | 95.7519 ns | 1,466.36 | 16.86 | 1.5717 | 0.0610 | 26496 B | 1,104.00 |
Array
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|
| Pure.DI composition root | 49.61 ns | 0.483 ns | 0.428 ns | 0.98 | 0.01 | 0.0244 | 0.0000 | 408 B | 1.00 |
| Hand Coded | 50.56 ns | 0.390 ns | 0.346 ns | 1.00 | 0.01 | 0.0244 | 0.0000 | 408 B | 1.00 |
| Pure.DI Resolve() | 51.30 ns | 0.933 ns | 0.827 ns | 1.01 | 0.02 | 0.0244 | 0.0000 | 408 B | 1.00 |
| Pure.DI Resolve(Type) | 55.49 ns | 0.742 ns | 0.657 ns | 1.10 | 0.01 | 0.0243 | 0.0000 | 408 B | 1.00 |
| LightInject | 55.79 ns | 0.795 ns | 0.744 ns | 1.10 | 0.02 | 0.0244 | 0.0000 | 408 B | 1.00 |
| DryIoc | 58.43 ns | 1.044 ns | 0.926 ns | 1.16 | 0.02 | 0.0243 | 0.0000 | 408 B | 1.00 |
| Unity | 3,264.04 ns | 61.563 ns | 127.139 ns | 64.55 | 2.53 | 0.8659 | 0.0076 | 14520 B | 35.59 |
| Autofac | 12,973.81 ns | 114.629 ns | 95.721 ns | 256.59 | 2.49 | 1.5717 | 0.0610 | 26496 B | 64.94 |






