<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nikolay Pianikov</title>
    <description>The latest articles on DEV Community by Nikolay Pianikov (@nikolayp).</description>
    <link>https://dev.to/nikolayp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3829170%2Feb4e9988-1bad-4bec-98c7-8a0b81504dd4.jpg</url>
      <title>DEV Community: Nikolay Pianikov</title>
      <link>https://dev.to/nikolayp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikolayp"/>
    <language>en</language>
    <item>
      <title>Pure.DI vs. Autofac: Code Generation or a Classic DI Container?</title>
      <dc:creator>Nikolay Pianikov</dc:creator>
      <pubDate>Fri, 24 Jul 2026 12:10:01 +0000</pubDate>
      <link>https://dev.to/nikolayp/puredi-vs-autofac-code-generation-or-a-classic-di-container-2ig4</link>
      <guid>https://dev.to/nikolayp/puredi-vs-autofac-code-generation-or-a-classic-di-container-2ig4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9rr4w07hnckhc9837onk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9rr4w07hnckhc9837onk.png" alt="Pure.DI vs Autofac" width="800" height="290"&gt;&lt;/a&gt;&lt;br&gt;
Search the web for “a powerful DI container for .NET,” and Autofac will appear on almost every list. It has a well-earned reputation as a mature, feature-rich container: lifetime scopes, modules, decorators, keyed services, factories, &lt;code&gt;Owned&amp;lt;T&amp;gt;&lt;/code&gt;, interception, and an extensible resolve pipeline.&lt;/p&gt;

&lt;p&gt;Against this background, Pure.DI is sometimes perceived as a specialized generator that wins on speed but must fall behind a classic DI container in functionality. That comparison needs qualification: the libraries solve many of the same application-level problems, but they do so at different stages of the application lifecycle.&lt;/p&gt;

&lt;p&gt;Pure.DI handles nearly the same everyday tasks as Autofac, but moves most of the container's work to compile time. From the setup, the generator builds an object graph—that is, a model of the dependencies—and uses it to generate code. The graph itself is a model, not a set of created instances. When the application accesses a root, the generated C# code creates the corresponding object composition.&lt;/p&gt;

&lt;p&gt;Autofac stores registrations in a container and creates objects through the universal &lt;code&gt;Resolve&lt;/code&gt; method at run time. Calling &lt;code&gt;Resolve&lt;/code&gt; directly from application code can easily lead to the Service Locator anti-pattern.&lt;/p&gt;

&lt;p&gt;Pure.DI works differently. It generates ordinary statically typed C# code and validates the dependency graph at compile time. Structural setup errors in the analyzed roots are therefore detected before the application starts. The public composition API consists of explicitly declared, strongly typed roots: properties or methods of that composition.&lt;/p&gt;

&lt;p&gt;The comparison below uses Pure.DI 2.5.2 and Autofac 9.3.1. The Autofac examples come from its official documentation. The Pure.DI examples come from its documentation and test scenarios.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Short Answer
&lt;/h2&gt;

&lt;p&gt;When the application structure is known at compile time, Pure.DI covers the core Autofac feature set, detects structural errors in the statically analyzed graphs of declared roots, and eliminates the overhead of resolving dependencies through a classic DI container.&lt;/p&gt;

&lt;p&gt;Autofac's main advantage appears when the dependency graph is determined at run time—for example, when plugins are loaded as assemblies while the application is running, registrations come from external configuration, or implementations are not known in advance.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;When the priority is&lt;/th&gt;
&lt;th&gt;Preferred option&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A statically known graph, build-time validation, Native AOT, and minimal object-creation cost&lt;/td&gt;
&lt;td&gt;Pure.DI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plugins, dynamic registrations, and run-time resolve pipelines&lt;/td&gt;
&lt;td&gt;Autofac&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ready-made multitenancy infrastructure and the extension ecosystem of a classic DI container&lt;/td&gt;
&lt;td&gt;Autofac&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typed roots and readable object-creation code&lt;/td&gt;
&lt;td&gt;Pure.DI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  One Task, Two Architectures
&lt;/h2&gt;

&lt;p&gt;A typical Autofac registration looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ContainerBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SingleInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IOrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resolve&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IOrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At run time, &lt;code&gt;Build()&lt;/code&gt; creates an Autofac container containing the registration descriptions. When &lt;code&gt;Resolve&amp;lt;IOrderService&amp;gt;()&lt;/code&gt; is called, the container selects a constructor, locates dependencies, applies lifetime rules, and creates the object composition.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/register/registration.html" rel="noopener noreferrer"&gt;Autofac registration concepts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The equivalent Pure.DI setup is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Lifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IOrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IOrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"OrderService"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/injections-of-abstractions.md" rel="noopener noreferrer"&gt;injection of abstractions in Pure.DI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Both snippets use the same domain types: &lt;code&gt;ILogger&lt;/code&gt;, &lt;code&gt;ConsoleLogger&lt;/code&gt;, &lt;code&gt;IOrderService&lt;/code&gt;, and &lt;code&gt;OrderService&lt;/code&gt;. The domain types are omitted because only the DI setup is being compared.&lt;/p&gt;

&lt;p&gt;The setups look similar, but the approaches differ substantially. For the &lt;code&gt;OrderService&lt;/code&gt; composition root, Pure.DI generates C# code for creating the object composition, conceptually similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IOrderService&lt;/span&gt; &lt;span class="n"&gt;OrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;get&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_singletonLogger&lt;/span&gt; &lt;span class="p"&gt;??=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_singletonLogger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pure.DI does not search registrations or bindings at run time. The generator has already selected the types and constructors, and an error in the dependency graph becomes a compilation error rather than a run-time error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Autofac&lt;/th&gt;
&lt;th&gt;Pure.DI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Constructor injection&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes, with compile-time validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Property, field, and method injection&lt;/td&gt;
&lt;td&gt;Yes for &lt;a href="https://docs.autofac.org/en/latest/register/prop-method-injection.html" rel="noopener noreferrer"&gt;properties and methods&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Yes for &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/property-injection.md" rel="noopener noreferrer"&gt;properties&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/field-injection.md" rel="noopener noreferrer"&gt;fields&lt;/a&gt;, and &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/method-injection.md" rel="noopener noreferrer"&gt;methods&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Transient&lt;/code&gt; and &lt;code&gt;Singleton&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lifetime scopes and &lt;code&gt;Scoped&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Named and matching scopes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Separate and dependent compositions, &lt;code&gt;SetupScope&lt;/code&gt;, tags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delegates and factories&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Func&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;Func&amp;lt;X, Y, T&amp;gt;&lt;/code&gt;, custom delegate factories, lambda registrations&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Func&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;Func&amp;lt;X, Y, T&amp;gt;&lt;/code&gt;, factories with arguments, strongly typed root methods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deferred creation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collections of implementations&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.autofac.org/en/latest/resolve/relationships.html#enumeration-ienumerable-b-ilist-b-icollection-b" rel="noopener noreferrer"&gt;&lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; and other relationship types&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/array.md" rel="noopener noreferrer"&gt;Arrays&lt;/a&gt;, lists, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/enumerable.md" rel="noopener noreferrer"&gt;enumerables&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/dictionary.md" rel="noopener noreferrer"&gt;dictionaries&lt;/a&gt;, and ranges&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Named and keyed services&lt;/td&gt;
&lt;td&gt;Names, keys, &lt;code&gt;IIndex&amp;lt;TKey,T&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Tags, smart tags, &lt;code&gt;IReadOnlyDictionary&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt;, &lt;code&gt;IKeyedServiceProvider&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generic bindings&lt;/td&gt;
&lt;td&gt;Open generic type registrations&lt;/td&gt;
&lt;td&gt;Precise generic bindings with marker types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generic roots with type constraints&lt;/td&gt;
&lt;td&gt;No dedicated API: the container resolves an already closed generic type&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/generic-composition-roots-with-constraints.md" rel="noopener noreferrer"&gt;Yes&lt;/a&gt;; the root is generated as a generic C# method&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decorators&lt;/td&gt;
&lt;td&gt;Built-in decorator registration&lt;/td&gt;
&lt;td&gt;Ordinary bindings and tags processed at compile time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interception&lt;/td&gt;
&lt;td&gt;Integration with Castle DynamicProxy&lt;/td&gt;
&lt;td&gt;Generated interception methods; Castle DynamicProxy when needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Owned&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A separate lifetime scope&lt;/td&gt;
&lt;td&gt;A separate ownership model for the object composition being created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Synchronous and asynchronous disposal&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.autofac.org/en/latest/lifetime/disposal.html" rel="noopener noreferrer"&gt;Yes&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/tracking-async-disposable-instances-per-a-composition-root.md" rel="noopener noreferrer"&gt;Yes&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Building up an existing object&lt;/td&gt;
&lt;td&gt;&lt;code&gt;InjectProperties()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Properties, fields, and methods through &lt;code&gt;BuildUp&lt;/code&gt;/&lt;code&gt;TryBuildUp&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modules&lt;/td&gt;
&lt;td&gt;Run-time modules&lt;/td&gt;
&lt;td&gt;Dependent, internal, and global compositions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.autofac.org/en/latest/register/scanning.html" rel="noopener noreferrer"&gt;Assembly scanning&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Yes, at run time&lt;/td&gt;
&lt;td&gt;No: types must be known at compile time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.autofac.org/en/latest/advanced/registration-sources.html" rel="noopener noreferrer"&gt;Registration sources&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Yes, registrations can be supplied dynamically&lt;/td&gt;
&lt;td&gt;No direct equivalent: the graph is fixed at compile time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.autofac.org/en/latest/advanced/pipelines.html" rel="noopener noreferrer"&gt;Resolve pipelines&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Yes, middleware for services and registrations&lt;/td&gt;
&lt;td&gt;No run-time pipeline; generated interception methods are available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.autofac.org/en/latest/configuration/index.html" rel="noopener noreferrer"&gt;Registration configuration&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Modules and external run-time configuration&lt;/td&gt;
&lt;td&gt;Setups are part of the source code and require recompilation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.autofac.org/en/latest/advanced/metadata.html" rel="noopener noreferrer"&gt;&lt;code&gt;Meta&amp;lt;T&amp;gt;&lt;/code&gt; metadata&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Yes, including strongly typed metadata&lt;/td&gt;
&lt;td&gt;No direct &lt;code&gt;Meta&amp;lt;T&amp;gt;&lt;/code&gt; equivalent; compile-time tags and attributes are available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.autofac.org/en/latest/advanced/multitenant.html" rel="noopener noreferrer"&gt;Multitenant containers&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ready-made run-time infrastructure&lt;/td&gt;
&lt;td&gt;No direct equivalent; compositions can be created for individual tenants&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.autofac.org/en/latest/advanced/pooled-instances.html" rel="noopener noreferrer"&gt;Pooled instances&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ready-made pooling package&lt;/td&gt;
&lt;td&gt;No built-in lifetime; an &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/object-pool.md" rel="noopener noreferrer"&gt;object-pool example&lt;/a&gt; implements the pattern in application code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compile-time validation of declared root graphs&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/DIAGNOSTICS.md" rel="noopener noreferrer"&gt;Yes&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explicit typed roots&lt;/td&gt;
&lt;td&gt;Not the primary model&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/composition-roots.md" rel="noopener noreferrer"&gt;Yes&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typed root arguments&lt;/td&gt;
&lt;td&gt;No dedicated root API; &lt;code&gt;Resolve&lt;/code&gt; parameters and delegate factories are available&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/root-arguments.md" rel="noopener noreferrer"&gt;Yes&lt;/a&gt;; roots are generated as ordinary methods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native AOT&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.autofac.org/en/latest/advanced/native-aot-trimming.html" rel="noopener noreferrer"&gt;Supported with limitations&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Generated object-creation code does not require .NET Reflection; see the &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/ConsoleNativeAOT.md" rel="noopener noreferrer"&gt;Native AOT example&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code&gt;ref struct&lt;/code&gt; in the graph&lt;/td&gt;
&lt;td&gt;Not available as universal relationship types&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/span-and-readonlyspan.md" rel="noopener noreferrer"&gt;Yes&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ref&lt;/code&gt; dependencies and stack-only root arguments&lt;/td&gt;
&lt;td&gt;The container's universal object-based API cannot pass managed references or non-boxable &lt;code&gt;ref struct&lt;/code&gt; values; external typed code is required&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/ref-dependencies.md" rel="noopener noreferrer"&gt;Yes&lt;/a&gt;, including generated root methods with &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/method-injection-for-a-hot-path.md" rel="noopener noreferrer"&gt;&lt;code&gt;scoped ReadOnlySpan&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Union types as DI contracts&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/union-types.md" rel="noopener noreferrer"&gt;Yes&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface generation&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/generate-an-interface-from-a-class.md" rel="noopener noreferrer"&gt;Yes&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nullable annotations as part of a DI contract&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;T&lt;/code&gt; and &lt;code&gt;T?&lt;/code&gt; are represented by the same &lt;code&gt;System.Type&lt;/code&gt; and cannot be distinguished by &lt;code&gt;Resolve(Type)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/nullable-reference-types.md" rel="noopener noreferrer"&gt;Yes&lt;/a&gt;, validated throughout the graph&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inspecting and debugging object-creation code&lt;/td&gt;
&lt;td&gt;Internal run-time pipeline&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/composition-roots.md" rel="noopener noreferrer"&gt;Ordinary generated C#&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The upper part of the table lists shared features, followed by Autofac features without a direct Pure.DI equivalent and Pure.DI features without a direct Autofac equivalent. Some rows describe different ways to solve the same application-level problem rather than direct counterparts. For example, an Autofac lifetime scope and a Pure.DI dependent composition have different semantics, although both help define boundaries for object creation and ownership.&lt;/p&gt;

&lt;h2&gt;
  
  
  Registrations, Lifetimes, and Implementation Selection
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lifetimes
&lt;/h3&gt;

&lt;p&gt;Autofac supports the following instance scopes: &lt;code&gt;Instance Per Dependency&lt;/code&gt;, &lt;code&gt;Single Instance&lt;/code&gt;, &lt;code&gt;Instance Per Lifetime Scope&lt;/code&gt;, &lt;code&gt;Instance Per Matching Lifetime Scope&lt;/code&gt;, &lt;code&gt;Instance Per Request&lt;/code&gt;, &lt;code&gt;Instance Per Owned&lt;/code&gt;, and &lt;code&gt;Thread Scope&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/lifetime/instance-scope.html" rel="noopener noreferrer"&gt;Autofac instance scopes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pure.DI supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Transient&lt;/code&gt;: a new instance for each injection;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Singleton&lt;/code&gt;: one instance per composition;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Scoped&lt;/code&gt;: one instance per scope;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PerResolve&lt;/code&gt;: one instance per root construction;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PerBlock&lt;/code&gt;: reuse within a block of generated code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sources: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/transient.md" rel="noopener noreferrer"&gt;Transient&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/singleton.md" rel="noopener noreferrer"&gt;Singleton&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/scoped.md" rel="noopener noreferrer"&gt;Scoped&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/perresolve.md" rel="noopener noreferrer"&gt;PerResolve&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/perblock.md" rel="noopener noreferrer"&gt;PerBlock&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pure.DI's advantage here is that the generator knows in advance where each object is stored and creates a field or local variable for it. Lifetime violations (captive dependencies), missing bindings, and dependency cycles are detected before the program starts. If an analyzed graph contains a structural error, the application will not compile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keyed Services and Tags
&lt;/h3&gt;

&lt;p&gt;Autofac can register a service by name or by an arbitrary key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OnlineState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Keyed&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDeviceState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;DeviceState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Online&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OfflineState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Keyed&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDeviceState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;DeviceState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Offline&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For run-time selection, Autofac provides &lt;code&gt;IIndex&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/advanced/keyed-services.html" rel="noopener noreferrer"&gt;named and keyed services in Autofac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pure.DI uses tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDeviceState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;DeviceState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Online&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OnlineState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDeviceState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;DeviceState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Offline&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OfflineState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDeviceState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Online"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DeviceState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Online&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/tags.md" rel="noopener noreferrer"&gt;tags in Pure.DI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This example uses the same &lt;code&gt;IDeviceState&lt;/code&gt;, &lt;code&gt;OnlineState&lt;/code&gt;, &lt;code&gt;OfflineState&lt;/code&gt;, and &lt;code&gt;DeviceState&lt;/code&gt; types as the Autofac snippet. A tag can be a string, enum value, type, &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;default&lt;/code&gt;, or a &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/smart-tags.md" rel="noopener noreferrer"&gt;smart tag&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the example above, Pure.DI selects the root implementation by tag while generating the code. If a consumer needs to select one of the registered implementations by key at run time, it can receive an ordinary &lt;code&gt;IReadOnlyDictionary&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unique&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;OnlineState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;KeyValuePair&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DeviceState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IDeviceState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;DeviceState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Online&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unique&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;OfflineState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;KeyValuePair&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DeviceState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IDeviceState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;DeviceState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Offline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DeviceStateSelector&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"DeviceStateSelector"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeviceStateSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;IReadOnlyDictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DeviceState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IDeviceState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IDeviceState&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DeviceState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pure.DI generates the code that builds the dictionary in advance and validates the graph of every value, while the specific element is selected by key at run time. This contract does not couple the application class to a DI tool's API and serves a purpose similar to Autofac's &lt;code&gt;IIndex&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/dictionary.md" rel="noopener noreferrer"&gt;dictionary injection in Pure.DI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When the standard .NET API is required, a Pure.DI composition can also implement &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/keyed-service-provider.md" rel="noopener noreferrer"&gt;&lt;code&gt;IKeyedServiceProvider&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generic Bindings
&lt;/h3&gt;

&lt;p&gt;Autofac registers an open generic type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterGeneric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&amp;gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&amp;gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/register/registration.html#open-generic-components" rel="noopener noreferrer"&gt;open generic components in Autofac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pure.DI uses a marker type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TT&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TT&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/generics.md" rel="noopener noreferrer"&gt;generic types in Pure.DI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The binding registration looks similar, but Pure.DI creates concrete code separately for &lt;code&gt;Repository&amp;lt;User&amp;gt;&lt;/code&gt; and &lt;code&gt;Repository&amp;lt;Order&amp;gt;&lt;/code&gt;. This allows it to account for generic constraints, nested generic types, and tags—in other words, to determine the precise object type at compile time. A successful build confirms the structural correctness of the generated graph, but it does not rule out application exceptions in constructors, factories, interceptors, or resource-disposal code.&lt;/p&gt;

&lt;p&gt;The distinction matters for Native AOT. The official Autofac documentation explicitly notes that an open generic type closed over a value type requires dynamic code at run time and can cause a &lt;code&gt;DependencyResolutionException&lt;/code&gt;. In Pure.DI, the concrete closed types are known at compile time, so this issue does not arise.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/advanced/native-aot-trimming.html" rel="noopener noreferrer"&gt;Native AOT and trimming in Autofac&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generic Roots
&lt;/h3&gt;

&lt;p&gt;Classic DI containers support registrations of open generic types, but resolve an already closed type such as &lt;code&gt;IRepository&amp;lt;Order&amp;gt;&lt;/code&gt;. Pure.DI can make the composition root itself generic and transfer the constraints of its type parameters into the public API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Hint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Hint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Off"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;StreamSource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TTDisposable&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DataProcessor&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TTDisposable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TTS&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDataProcessor&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TTDisposable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TTS&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"GetProcessor"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;processor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetProcessor&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IStreamSource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDisposable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StreamSource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IStreamSource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDisposable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IDataProcessor&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDisposable&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TOptions&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataProcessor&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;IStreamSource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDataProcessor&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDisposable&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TOptions&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this root, Pure.DI generates an ordinary generic method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IDataProcessor&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GetProcessor&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDisposable&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TOptions&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The calling code selects &lt;code&gt;T&lt;/code&gt; and &lt;code&gt;TOptions&lt;/code&gt;, and the C# compiler validates them. The universal &lt;code&gt;Resolve&lt;/code&gt; method of a classic DI container cannot express this contract because it accepts an already constructed closed type. An equivalent would require a separate, manually written generic factory outside the container API.&lt;/p&gt;

&lt;p&gt;A type parameter can determine not only the return type but also the type of a root argument. For example, combining &lt;code&gt;RootArg&amp;lt;TT&amp;gt;("model")&lt;/code&gt; with &lt;code&gt;Root&amp;lt;IPresenter&amp;lt;TT&amp;gt;&amp;gt;("GetPresenter")&lt;/code&gt; creates a method in which the model is supplied at run time and type correspondence is checked by the compiler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IPresenter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GetPresenter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A root can also be asynchronous, accept ordinary run-time arguments, and preserve type constraints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IQuery&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TConnection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;GetDataQueryAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TConnection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TConnection&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDisposable&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TResult&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are not new variations of &lt;code&gt;Resolve&lt;/code&gt;: Pure.DI generates typed methods whose signatures become part of the composition API.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/generic-composition-roots-with-constraints.md" rel="noopener noreferrer"&gt;Pure.DI generic roots with type constraints&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/generic-root-arguments.md" rel="noopener noreferrer"&gt;generic root arguments&lt;/a&gt;, and &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/generic-async-composition-roots-with-constraints.md" rel="noopener noreferrer"&gt;asynchronous generic roots with constraints&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Factories and Relationship Types
&lt;/h2&gt;

&lt;p&gt;Autofac calls &lt;code&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;Owned&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;Func&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;Meta&amp;lt;T&amp;gt;&lt;/code&gt;, and &lt;code&gt;IIndex&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt; relationship types. They can be combined—for example, as &lt;code&gt;IEnumerable&amp;lt;Func&amp;lt;Owned&amp;lt;T&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/resolve/relationships.html" rel="noopener noreferrer"&gt;implicit relationship types in Autofac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pure.DI supports the same core scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/lazy.md" rel="noopener noreferrer"&gt;Lazy&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/func.md" rel="noopener noreferrer"&gt;Func&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/func-with-arguments.md" rel="noopener noreferrer"&gt;Func with arguments&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/enumerable.md" rel="noopener noreferrer"&gt;enumerables&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/dictionary.md" rel="noopener noreferrer"&gt;dictionaries&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/composition-arguments.md" rel="noopener noreferrer"&gt;composition arguments&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/root-arguments.md" rel="noopener noreferrer"&gt;root arguments&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, both libraries automatically inject &lt;code&gt;Func&amp;lt;IEnemy&amp;gt;&lt;/code&gt; into the same consumer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GameLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnemy&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;enemyFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IGameLevel&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnemy&lt;/span&gt; &lt;span class="nf"&gt;CreateEnemy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;enemyFactory&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Autofac registration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Enemy&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnemy&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GameLevel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IGameLevel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pure.DI registration and root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Enemy&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GameLevel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IGameLevel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"GameLevel"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/injection-on-demand.md" rel="noopener noreferrer"&gt;on-demand injection in Pure.DI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In Autofac, the delegate is a relationship type. In Pure.DI, it is generated as a strongly typed object-creation call. The application-level &lt;code&gt;GameLevel&lt;/code&gt; does not depend on the choice of DI tool.&lt;/p&gt;

&lt;p&gt;Pure.DI also supports &lt;code&gt;Func&amp;lt;...&amp;gt;&lt;/code&gt; with arguments whose values are determined dynamically at run time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Lifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Clock&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Team&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Team&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Team"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The consumer receives a factory with two dynamic arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Team&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IPerson&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;personFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IPerson&lt;/span&gt; &lt;span class="nf"&gt;CreatePerson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&gt;personFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt; values are supplied when the factory is called. Pure.DI obtains the &lt;code&gt;IClock&lt;/code&gt; dependency, which &lt;code&gt;Person&lt;/code&gt; also needs, from the composition. Statically known dependencies are therefore validated at compile time, while dynamic data is passed at run time.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/func-with-arguments.md" rel="noopener noreferrer"&gt;&lt;code&gt;Func&lt;/code&gt; with arguments in Pure.DI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Autofac also supports &lt;code&gt;Func&amp;lt;X, Y, T&amp;gt;&lt;/code&gt; with run-time arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Clock&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IClock&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;SingleInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPerson&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;personFactory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resolve&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IPerson&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;personFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Nik"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the standard &lt;code&gt;Func&amp;lt;X, Y, T&amp;gt;&lt;/code&gt;, Autofac matches arguments to constructor parameters by type and obtains the remaining dependencies from the current lifetime scope. Such a &lt;code&gt;Func&lt;/code&gt; does not support multiple arguments of the same type. For that case, Autofac provides custom delegate factories, whose parameters are matched by name.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://docs.autofac.org/en/latest/resolve/relationships.html#parameterized-instantiation-func-x-y-b" rel="noopener noreferrer"&gt;&lt;code&gt;Func&amp;lt;X, Y, T&amp;gt;&lt;/code&gt; in Autofac&lt;/a&gt; and &lt;a href="https://docs.autofac.org/en/latest/advanced/delegate-factories.html" rel="noopener noreferrer"&gt;delegate factories&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Owned&amp;lt;T&amp;gt;&lt;/code&gt;: Same Name, Different Implementation
&lt;/h2&gt;

&lt;p&gt;Both libraries allow a consumer to explicitly own part of the object composition being created and dispose of it independently of the container or main composition. For example, the consumer can use a dependency such as &lt;code&gt;Func&amp;lt;Owned&amp;lt;IMessageHandler&amp;gt;&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MessagePump&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Owned&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMessageHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;handlerFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MessagePump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Owned&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMessageHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;handlerFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handlerFactory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;handlerFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;NextMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;handlerFactory&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a form of the official example from the Autofac documentation on &lt;a href="https://docs.autofac.org/en/latest/advanced/owned-instances.html" rel="noopener noreferrer"&gt;owned instances&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Autofac Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MessageHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMessageHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// MessagePump receives Func&amp;lt;Owned&amp;lt;IMessageHandler&amp;gt;&amp;gt;.&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MessagePump&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pump&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resolve&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MessagePump&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For each &lt;code&gt;Owned&amp;lt;T&amp;gt;&lt;/code&gt;, Autofac creates a separate &lt;code&gt;ILifetimeScope&lt;/code&gt;. Disposing &lt;code&gt;Owned&amp;lt;T&amp;gt;&lt;/code&gt; ends the scope and disposes dependencies created within it that are not shared.&lt;/p&gt;

&lt;p&gt;In Autofac 9.3.1, &lt;code&gt;Owned&amp;lt;T&amp;gt;&lt;/code&gt; is a class. It replaces the lifetime-scope reference through &lt;code&gt;Interlocked.Exchange&lt;/code&gt;, supports &lt;code&gt;Dispose()&lt;/code&gt; and &lt;code&gt;DisposeAsync()&lt;/code&gt;, and then delegates disposal to the lifetime scope.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/autofac/Autofac/blob/v9.3.1/src/Autofac/Features/OwnedInstances/Owned.cs" rel="noopener noreferrer"&gt;&lt;code&gt;Owned.cs&lt;/code&gt; from Autofac 9.3.1&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pure.DI Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMessageHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MessageHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// MessagePump receives Func&amp;lt;Owned&amp;lt;IMessageHandler&amp;gt;&amp;gt;.&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MessagePump&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MessagePump&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"MessagePump"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pump&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MessagePump&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neither setup requires &lt;code&gt;Owned&amp;lt;T&amp;gt;&lt;/code&gt; to be registered separately. Autofac supplies it as a relationship type, while Pure.DI recognizes &lt;code&gt;Func&amp;lt;Owned&amp;lt;IMessageHandler&amp;gt;&amp;gt;&lt;/code&gt; in the &lt;code&gt;MessagePump&lt;/code&gt; constructor and generates the factory together with disposal code for the object composition being created.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/tracking-disposable-instances-in-delegates.md" rel="noopener noreferrer"&gt;tracking disposable instances in Pure.DI delegates&lt;/a&gt; and &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/tests/Pure.DI.IntegrationTests/OwnedTests.cs" rel="noopener noreferrer"&gt;OwnedTests.cs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Internally, however, there is no specialized lifetime scope as in Autofac:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Owned&amp;lt;T&amp;gt;&lt;/code&gt; is a &lt;code&gt;readonly struct&lt;/code&gt; containing the value and a reference to the disposal mechanism;&lt;/li&gt;
&lt;li&gt;the generator knows all disposable objects in the specific dependency graph in advance;&lt;/li&gt;
&lt;li&gt;a graph without resources uses a shared empty owner without a separate resource accumulator;&lt;/li&gt;
&lt;li&gt;when the number of resources is known, the accumulator is created with an appropriate capacity, optimizing the list used to store resources;&lt;/li&gt;
&lt;li&gt;with &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/threadsafe-off-for-single-thread-composition.md" rel="noopener noreferrer"&gt;&lt;code&gt;ThreadSafe = Off&lt;/code&gt;&lt;/a&gt;, the generator does not add unnecessary synchronization;&lt;/li&gt;
&lt;li&gt;synchronous and asynchronous disposal are generated with the target .NET platform in mind.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The implementation handles synchronous and asynchronous disposal, reverse resource order, repeated and concurrent calls, nested owners, shared lifetimes, and rollback when graph creation fails. These scenarios are covered by comparative integration tests against Autofac.&lt;/p&gt;

&lt;p&gt;Autofac provides a similar outcome in equivalent scenarios. Pure.DI's advantage here is that lifetime management is implemented in the generated code that creates the object composition. This approach does not require a special lifetime scope like Autofac and enables compile-time optimizations that are unavailable to a classic DI container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Property and Method Injection, and Building Up Objects
&lt;/h2&gt;

&lt;p&gt;Autofac supports required properties, &lt;code&gt;PropertiesAutowired()&lt;/code&gt;, manual property assignment, and injection into an existing object through &lt;code&gt;InjectProperties()&lt;/code&gt;. Method injection is usually configured with a lambda registration.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/register/prop-method-injection.html" rel="noopener noreferrer"&gt;property and method injection in Autofac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pure.DI supports injection through properties, fields, and methods directly in the generated code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Navigator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;INavigator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IMap&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;CurrentMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Dependency&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;LoadMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMap&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CurrentMap&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/method-injection.md" rel="noopener noreferrer"&gt;method injection in Pure.DI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Build-Up is available for an existing object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuildUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;TryBuildUp&lt;/code&gt; lets you safely check whether an object can be built up.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/build-up-of-an-existing-object.md" rel="noopener noreferrer"&gt;building up an existing object&lt;/a&gt; and &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/builders.md" rel="noopener noreferrer"&gt;builders with TryBuildUp&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Because the members are known to the generator at compile time, objects are completed directly by ordinary C# code and do not require .NET Reflection at run time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decorators and Interception
&lt;/h2&gt;

&lt;p&gt;Autofac has a dedicated API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TextWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterDecorator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BoxWidget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/advanced/adapters-decorators.html" rel="noopener noreferrer"&gt;Autofac adapters and decorators&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In Pure.DI, the same chain is configured as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TextWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BoxWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Widget"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both cases use &lt;code&gt;TextWidget&lt;/code&gt; and &lt;code&gt;BoxWidget&lt;/code&gt;. The only difference in the DI setup is that Pure.DI explicitly tags the decorated service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BoxWidget&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;IWidget&lt;/span&gt; &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IWidget&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"[ &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Render&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s"&gt; ]"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;"base"&lt;/code&gt; tag is chosen arbitrarily for its meaning; it is not a “magic” value.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/decorator.md" rel="noopener noreferrer"&gt;decorators in Pure.DI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This approach requires no special decorator logic at run time: decorators become part of the ordinary object composition, and the corresponding dependency graph is validated at compile time. For example, a single decorator class can decorate several objects of different types at once.&lt;/p&gt;

&lt;p&gt;For interception, Autofac integrates with Castle DynamicProxy.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/advanced/interceptors.html" rel="noopener noreferrer"&gt;type interception in Autofac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pure.DI provides the generated partial methods &lt;code&gt;OnNewInstance&lt;/code&gt; and &lt;code&gt;OnDependencyInjection&lt;/code&gt;. They can replace or wrap an instance at a selected point in the object composition without sacrificing performance. When a dynamic proxy object is genuinely required, Castle DynamicProxy can be integrated into the generated object-composition code.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/interception.md" rel="noopener noreferrer"&gt;interception in Pure.DI&lt;/a&gt; and &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/advanced-interception.md" rel="noopener noreferrer"&gt;advanced interception&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Autofac is more convenient when interception must be configured entirely at run time. Pure.DI is stronger when performance, transparency, and generation-time type filtering matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modules and Large Compositions
&lt;/h2&gt;

&lt;p&gt;Autofac modules encapsulate a set of registrations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RepositoryModule&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Module&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ContainerBuilder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SqlRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/configuration/modules.html" rel="noopener noreferrer"&gt;Autofac modules&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pure.DI splits the setup of large compositions into several parts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Infrastructure"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CompositionKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Internal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDatabase&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SqlDatabase&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DependsOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Infrastructure"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"UserService"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sources: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/dependent-compositions.md" rel="noopener noreferrer"&gt;dependent compositions&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/global-compositions.md" rel="noopener noreferrer"&gt;global compositions&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/inheritance-of-compositions.md" rel="noopener noreferrer"&gt;composition inheritance&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/dependent-compositions-with-setup-context.md" rel="noopener noreferrer"&gt;setup context&lt;/a&gt;, and &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/scope-setup-method.md" rel="noopener noreferrer"&gt;scope setup&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Compositions may be declared &lt;code&gt;public&lt;/code&gt;, &lt;code&gt;internal&lt;/code&gt;, or &lt;code&gt;global&lt;/code&gt;. Composition inheritance, setup contexts, and separate scope setups are also available. These features make configuration reusable, and everything is processed at compile time.&lt;/p&gt;

&lt;p&gt;Dynamic Autofac modules are better suited to assembling an application from a set of plugins that is not known in advance. Pure.DI compositions are better suited to modules defined at compile time: the graphs of declared roots are validated during the build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Pure.DI Is Particularly Strong
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A Graph Error Becomes a Build Error
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Build()&lt;/code&gt; completes component registration and prepares the Autofac container, but does not perform full graph validation. Because registrations, parameters, modules, and factories can be dynamic, many errors are discovered only by a specific &lt;code&gt;Resolve()&lt;/code&gt; call. A classic DI container therefore cannot always confirm in advance that every object composition created at run time will be valid.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/faq/container-analysis.html" rel="noopener noreferrer"&gt;why registration analysis is not built into Autofac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pure.DI analyzes every declared root and reports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;missing bindings;&lt;/li&gt;
&lt;li&gt;ambiguous implementation selection;&lt;/li&gt;
&lt;li&gt;cyclic dependencies;&lt;/li&gt;
&lt;li&gt;violations of generic type constraints;&lt;/li&gt;
&lt;li&gt;an inaccessible constructor or other type member;&lt;/li&gt;
&lt;li&gt;an incompatible tag;&lt;/li&gt;
&lt;li&gt;nullable annotation issues;&lt;/li&gt;
&lt;li&gt;invalid use of stack-only types;&lt;/li&gt;
&lt;li&gt;and much more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The developer gets structurally validated object-composition code before the application starts. Application code in constructors, factories, and interceptors retains its ordinary behavior and can still throw an exception at run time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Composition Roots
&lt;/h3&gt;

&lt;p&gt;In Autofac, the typical object-composition scenario is based on calling &lt;code&gt;Resolve&amp;lt;T&amp;gt;()&lt;/code&gt;. In Pure.DI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateReport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A composition root can be a property or a method with typed arguments and a typed result. Anonymous roots remain available through &lt;code&gt;Resolve&lt;/code&gt; methods when needed, just as in Autofac.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/composition-roots.md" rel="noopener noreferrer"&gt;composition roots&lt;/a&gt; and &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/root-arguments.md" rel="noopener noreferrer"&gt;typed root arguments&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Native AOT Without Reflection in Generated Code
&lt;/h3&gt;

&lt;p&gt;Autofac 9 supports trimming and Native AOT for basic scenarios, but documents the following limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;assembly scanning requires preserving types found through .NET Reflection;&lt;/li&gt;
&lt;li&gt;an open generic type closed over a value type can produce a run-time error;&lt;/li&gt;
&lt;li&gt;strongly typed metadata views require run-time expression compilation;&lt;/li&gt;
&lt;li&gt;generated factories, delegate factories, and custom property selectors can produce &lt;code&gt;RequiresDynamicCode&lt;/code&gt; or &lt;code&gt;RequiresUnreferencedCode&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;the absence of a warning for some relationship types does not guarantee full AOT compatibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/advanced/native-aot-trimming.html" rel="noopener noreferrer"&gt;Native AOT and trimming in Autofac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pure.DI generates ordinary &lt;code&gt;new&lt;/code&gt; calls. To the trimmer, this is normal statically linked C# code: the generator does not need to preserve constructors, methods, properties, and fields “just in case,” because everything required is already used directly. Application compatibility with Native AOT and trimming still depends on custom factories, interceptors, and third-party libraries.&lt;/p&gt;

&lt;p&gt;Example: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/ConsoleNativeAOT.md" rel="noopener noreferrer"&gt;a Pure.DI console application with Native AOT&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modern C# Becomes Part of the Object Composition
&lt;/h3&gt;

&lt;p&gt;Pure.DI supports scenarios that cannot be represented through the universal object-based API of most classic DI containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code&gt;ReadOnlySpan&amp;lt;T&amp;gt;&lt;/code&gt; as dependencies;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ref struct&lt;/code&gt; and factories with &lt;code&gt;allows ref struct&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ref&lt;/code&gt; dependencies that preserve managed-reference semantics;&lt;/li&gt;
&lt;li&gt;stack-only root arguments and generated &lt;code&gt;scoped ReadOnlySpan&amp;lt;T&amp;gt;&lt;/code&gt; parameters;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ValueTask&amp;lt;T&amp;gt;&lt;/code&gt; roots;&lt;/li&gt;
&lt;li&gt;nullable annotations as part of a DI contract;&lt;/li&gt;
&lt;li&gt;union types as DI contracts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In particular, &lt;code&gt;T&lt;/code&gt; and &lt;code&gt;T?&lt;/code&gt; have the same run-time type. A universal &lt;code&gt;Resolve(Type)&lt;/code&gt; therefore cannot select between them as two separate contracts. Pure.DI analyzes nullable annotations in source code and preserves the distinction while validating and generating the graph.&lt;/p&gt;

&lt;p&gt;An object-based API also cannot accept &lt;code&gt;ReadOnlySpan&amp;lt;T&amp;gt;&lt;/code&gt; through &lt;code&gt;object&lt;/code&gt; or &lt;code&gt;params object[]&lt;/code&gt;: a stack-only value cannot be boxed. Pure.DI can instead generate a typed root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IRouteMatcher&lt;/span&gt; &lt;span class="nf"&gt;CreateMatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;scoped&lt;/span&gt; &lt;span class="n"&gt;ReadOnlySpan&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Managed references are also preserved when dependencies are injected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Ordinal&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Such an API can be written manually as an external factory, but it cannot be expressed through the universal object-based API of a classic DI container.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/span-and-readonlyspan.md" rel="noopener noreferrer"&gt;Span and ReadOnlySpan&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/ref-dependencies.md" rel="noopener noreferrer"&gt;&lt;code&gt;ref&lt;/code&gt; dependencies&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/allows-ref-struct-factory.md" rel="noopener noreferrer"&gt;a factory with &lt;code&gt;allows ref struct&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/method-injection-for-a-hot-path.md" rel="noopener noreferrer"&gt;method injection on a hot path&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/default-func-with-readonlyspan.md" rel="noopener noreferrer"&gt;a factory with &lt;code&gt;ReadOnlySpan&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/valuetask-root.md" rel="noopener noreferrer"&gt;a &lt;code&gt;ValueTask&amp;lt;T&amp;gt;&lt;/code&gt; root&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/nullable-reference-types.md" rel="noopener noreferrer"&gt;nullable reference types&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/union-types.md" rel="noopener noreferrer"&gt;union types&lt;/a&gt;, and &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/non-boxing-union-result.md" rel="noopener noreferrer"&gt;a non-boxing union result&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pure.DI can also &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/generate-an-interface-from-a-class.md" rel="noopener noreferrer"&gt;generate interfaces from a class&lt;/a&gt;, preserving generic types, nullable annotations, events, and XML documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance: The Overhead of a Classic DI Container
&lt;/h2&gt;

&lt;p&gt;The BenchmarkDotNet suite in the repository compares creation of the same object composition by hand, through Pure.DI, and through Autofac—a classic DI container.&lt;/p&gt;

&lt;p&gt;The following rows come from saved results produced with BenchmarkDotNet 0.15.8 and .NET SDK 10.0.102 on Windows 10 with an AMD Ryzen 9 5900X. The table shows the mean time per operation and the amount of allocated managed memory:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Hand-written code&lt;/th&gt;
&lt;th&gt;Pure.DI root&lt;/th&gt;
&lt;th&gt;Autofac&lt;/th&gt;
&lt;th&gt;Pure.DI memory&lt;/th&gt;
&lt;th&gt;Autofac memory&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Transient&lt;/td&gt;
&lt;td&gt;2.820 ns&lt;/td&gt;
&lt;td&gt;3.088 ns&lt;/td&gt;
&lt;td&gt;13,207.209 ns&lt;/td&gt;
&lt;td&gt;24 B&lt;/td&gt;
&lt;td&gt;30,424 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Singleton&lt;/td&gt;
&lt;td&gt;2.745 ns&lt;/td&gt;
&lt;td&gt;2.805 ns&lt;/td&gt;
&lt;td&gt;8,143.869 ns&lt;/td&gt;
&lt;td&gt;24 B&lt;/td&gt;
&lt;td&gt;22,048 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Func&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3.089 ns&lt;/td&gt;
&lt;td&gt;2.774 ns&lt;/td&gt;
&lt;td&gt;4,800.897 ns&lt;/td&gt;
&lt;td&gt;24 B&lt;/td&gt;
&lt;td&gt;13,128 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Array&lt;/td&gt;
&lt;td&gt;49.80 ns&lt;/td&gt;
&lt;td&gt;51.32 ns&lt;/td&gt;
&lt;td&gt;13,300.06 ns&lt;/td&gt;
&lt;td&gt;408 B&lt;/td&gt;
&lt;td&gt;26,496 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/DevTeam/Pure.DI/tree/91f5c2ee57dbccf2efa21959d7d8743ac1e9937e/benchmarks/data/results" rel="noopener noreferrer"&gt;Pure.DI performance benchmarks&lt;/a&gt;. The result snapshot was saved in commit &lt;code&gt;91f5c2ee57dbccf2efa21959d7d8743ac1e9937e&lt;/code&gt; on February 14, 2026.&lt;/p&gt;

&lt;p&gt;In this run, creating this particular transient graph through a generated Pure.DI root was approximately 4,276 times faster and allocated about 1/1,268 as much memory as resolving the same root through Autofac. These results apply to a specific object graph and benchmark environment. The ratio may change with a different graph structure, .NET version, hardware, or way of obtaining the root.&lt;/p&gt;

&lt;p&gt;The difference follows from the architecture of the tools. Pure.DI generates specialized graph-creation code in advance, close to hand-written code. Autofac resolves dependencies through the universal mechanism of a classic DI container and applies lifetime rules while the program is running. Pure.DI's advantage is therefore not a particular result from one benchmark, but minimal overhead when creating a statically known graph.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Autofac Is Objectively Stronger
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Assembly Scanning
&lt;/h3&gt;

&lt;p&gt;Autofac can load an assembly, find suitable implementations, and register them at run time.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/register/scanning.html" rel="noopener noreferrer"&gt;assembly scanning in Autofac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is useful for plugins and extensions. The trade-offs are reliance on .NET Reflection, less explicit configuration, and additional trimming requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Registration Sources and Resolve Pipelines
&lt;/h3&gt;

&lt;p&gt;Autofac can dynamically provide a registration for an unknown type and add middleware to the resolve pipeline.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://docs.autofac.org/en/latest/advanced/registration-sources.html" rel="noopener noreferrer"&gt;registration sources&lt;/a&gt; and &lt;a href="https://docs.autofac.org/en/latest/advanced/pipelines.html" rel="noopener noreferrer"&gt;resolve pipelines&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Frameworks and infrastructure libraries need this flexibility. In Pure.DI, similar behavior is usually implemented through a factory, an interception method, or specialized code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ready-Made Multitenancy Infrastructure
&lt;/h3&gt;

&lt;p&gt;Autofac has a dedicated multitenant package and tenant-specific containers.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://docs.autofac.org/en/latest/advanced/multitenant.html" rel="noopener noreferrer"&gt;multitenant applications in Autofac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pure.DI can create separate compositions for each tenant using tags, factories, composition arguments, or root arguments, but does not provide identical run-time capabilities.&lt;/p&gt;

&lt;p&gt;If an application genuinely uses these capabilities, Autofac may be the better choice. Most applications, however, do not need a dynamic dependency graph to create object compositions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Different Extensibility Models
&lt;/h2&gt;

&lt;p&gt;Autofac describes features in container terms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;registration sources;&lt;/li&gt;
&lt;li&gt;resolve pipelines;&lt;/li&gt;
&lt;li&gt;service middleware;&lt;/li&gt;
&lt;li&gt;lifetime scopes;&lt;/li&gt;
&lt;li&gt;module scanning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pure.DI often solves the same application-level problems with ordinary C# code, although these are not always direct equivalents of Autofac features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generic bindings or factories replace run-time registration sources;&lt;/li&gt;
&lt;li&gt;generated partial methods replace resolve middleware;&lt;/li&gt;
&lt;li&gt;an explicit typed decorator chain in the object composition replaces decorator discovery;&lt;/li&gt;
&lt;li&gt;a lifetime scope for a unit of work is replaced by a generated &lt;code&gt;Owned&amp;lt;T&amp;gt;&lt;/code&gt; optimized for a specific object composition;&lt;/li&gt;
&lt;li&gt;a dependent composition replaces a run-time module;&lt;/li&gt;
&lt;li&gt;ordinary properties or methods serve as composition roots instead of &lt;code&gt;Resolve&amp;lt;T&amp;gt;()&lt;/code&gt;. &lt;a href="https://github.com/DevTeam/Pure.DI/blob/2.5.2/readme/resolve-methods.md" rel="noopener noreferrer"&gt;&lt;code&gt;Resolve&amp;lt;T&amp;gt;()&lt;/code&gt; methods&lt;/a&gt; are also supported and are highly efficient.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Autofac is indeed a powerful classic DI container. Its maturity, ecosystem, and dynamic run-time configuration capabilities deserve recognition.&lt;/p&gt;

&lt;p&gt;That does not mean Pure.DI is less capable. For a .NET application with a dependency graph known in advance, the picture is different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pure.DI has counterparts for the core Autofac DI scenarios;&lt;/li&gt;
&lt;li&gt;the dependency graph for every used root is validated during the build;&lt;/li&gt;
&lt;li&gt;generated object-creation code does not require Reflection or dynamic code generation;&lt;/li&gt;
&lt;li&gt;composition roots are strongly typed;&lt;/li&gt;
&lt;li&gt;generated code can be read and debugged like ordinary C#;&lt;/li&gt;
&lt;li&gt;generated code does not need to box values for the sake of a classic DI container's universal API;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Owned&amp;lt;T&amp;gt;&lt;/code&gt; gets a specialized, thoroughly tested resource-ownership model;&lt;/li&gt;
&lt;li&gt;performance is close to hand-written code following the Pure DI paradigm.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A more precise formulation of the distinction is therefore:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pure.DI implements the core scenarios of a classic DI container through code generation and validates the structure of statically known graphs during the build.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Autofac is justified when the composition structure must change at run time. When it is known in advance, Pure.DI builds and validates the graph, then generates the code. That work happens once, while the application is being built.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>dependencyinversion</category>
      <category>cleancode</category>
      <category>ioc</category>
    </item>
    <item>
      <title>What's new in Pure.DI: union types, interface generation, and DI without unnecessary allocations</title>
      <dc:creator>Nikolay Pianikov</dc:creator>
      <pubDate>Sun, 19 Jul 2026 09:38:55 +0000</pubDate>
      <link>https://dev.to/nikolayp/whats-new-in-puredi-union-types-interface-generation-and-di-without-unnecessary-allocations-5m1</link>
      <guid>https://dev.to/nikolayp/whats-new-in-puredi-union-types-interface-generation-and-di-without-unnecessary-allocations-5m1</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fec63tnkji2odq3pjst8p.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fec63tnkji2odq3pjst8p.gif" alt="Pure.DI" width="720" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pure.DI is a dependency injection code generator that runs at compile time. It advances the idea of "pure DI": instead of a container and reflection, you get regular C# code that creates object compositions. This article covers the new features introduced in releases 2.3.5 through 2.5.1, from union types as DI contracts to dependency injection scenarios without unnecessary allocations.&lt;/p&gt;

&lt;p&gt;The key benefits of Pure.DI are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero overhead&lt;/strong&gt;: the generator produces regular, statically typed C# code without runtime dependency lookup or reflection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compile-time validation&lt;/strong&gt;: DI graph configuration errors, cyclic dependencies, and missing bindings are detected during compilation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broad compatibility&lt;/strong&gt;: from .NET Framework 2.0 to modern .NET versions, Unity, Native AOT, and other platforms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparency&lt;/strong&gt;: you can always inspect, debug, and understand the generated code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Six releases have shipped since the previous article, so this one is divided into two main parts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 1. New features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;union types as DI contracts&lt;/li&gt;
&lt;li&gt;generating interfaces from classes (&lt;code&gt;[GenerateInterface]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;implementation-level attribute bindings (&lt;code&gt;[Bind]&lt;/code&gt;, &lt;code&gt;[Type]&lt;/code&gt;, &lt;code&gt;[Tag]&lt;/code&gt;, &lt;code&gt;[Lifetime]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[Export]&lt;/code&gt;: class members as dependency sources&lt;/li&gt;
&lt;li&gt;custom binding attributes&lt;/li&gt;
&lt;li&gt;scopes: &lt;code&gt;SetupScope&lt;/code&gt;, per-scene Unity scopes, and &lt;code&gt;CreateScope()&lt;/code&gt; for Microsoft DI&lt;/li&gt;
&lt;li&gt;complete nullable reference type support&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TryBuildUp&lt;/code&gt; for safe object build-up&lt;/li&gt;
&lt;li&gt;support for new C# 13/14 features: &lt;code&gt;OverloadResolutionPriority&lt;/code&gt; and partial constructors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Part 2. Performance - DI on hot paths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;ReadOnlySpan&amp;lt;T&amp;gt;&lt;/code&gt;, and ref struct dependencies&lt;/li&gt;
&lt;li&gt;factories for stack-only values (&lt;code&gt;allows ref struct&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;zero-copy parsing and method injection on hot paths&lt;/li&gt;
&lt;li&gt;diagnostics for stack-only values&lt;/li&gt;
&lt;li&gt;non-boxing union results&lt;/li&gt;
&lt;li&gt;a catalog of high-performance examples: ArrayPool, object pools, closure-free factories, &lt;code&gt;ValueTask&amp;lt;T&amp;gt;&lt;/code&gt; roots, and &lt;code&gt;ThreadSafe = Off&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;leaner generated code&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 1. New features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Union types as DI contracts
&lt;/h3&gt;

&lt;p&gt;Preview versions of C# introduced union types: a type whose value can be one of several predefined case types. Pure.DI can now use a union as a &lt;strong&gt;DI contract&lt;/strong&gt;. A case implementation is implicitly converted to the union, and the generator builds the entire dependency graph of the selected case behind the union contract. Unlike an interface, case types do not need to share an abstraction. They can even be classes from unrelated third-party SDKs that you cannot modify.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IReceiptService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReceiptService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// The composition decides which payment gateway&lt;/span&gt;
    &lt;span class="c1"&gt;// stands behind the union contract&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PaymentGateway&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;StripeGateway&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CheckoutService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Checkout"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// The case types are independent classes with no common interface,&lt;/span&gt;
&lt;span class="c1"&gt;// and each one has its own dependencies&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StripeGateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StripeApiClient&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;amountInCents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"charge &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amountInCents&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankGateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BankAccount&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;amountInCents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="s"&gt;$"bank transfer &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amountInCents&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; from &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Iban&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// The case types are implicitly convertible to the union,&lt;/span&gt;
&lt;span class="c1"&gt;// and Pure.DI uses that conversion&lt;/span&gt;
&lt;span class="n"&gt;union&lt;/span&gt; &lt;span class="nf"&gt;PaymentGateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StripeGateway&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BankGateway&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// The service depends on the union and handles each case explicitly&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CheckoutService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentGateway&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IReceiptService&lt;/span&gt; &lt;span class="n"&gt;receipts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;amountInCents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;confirmation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;StripeGateway&lt;/span&gt; &lt;span class="n"&gt;stripe&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amountInCents&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;BankGateway&lt;/span&gt; &lt;span class="n"&gt;bank&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amountInCents&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidOperationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unknown payment gateway."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;receipts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;confirmation&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switching providers is a one-line change: &lt;code&gt;.Bind&amp;lt;PaymentGateway&amp;gt;().To&amp;lt;BankGateway&amp;gt;()&lt;/code&gt;. Important details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when there is no explicit union binding but exactly one registered binding can be converted to the union, Pure.DI resolves the union through that single case automatically&lt;/li&gt;
&lt;li&gt;when several case bindings are applicable, Pure.DI reports &lt;code&gt;DIE050&lt;/code&gt; with the candidate list and binding locations&lt;/li&gt;
&lt;li&gt;union types do &lt;strong&gt;not participate&lt;/strong&gt; in auto-binding, so Pure.DI cannot accidentally create an "empty" union&lt;/li&gt;
&lt;li&gt;the case binding retains ownership of its lifetime and disposal behavior; &lt;code&gt;Singleton&lt;/code&gt;, &lt;code&gt;Scoped&lt;/code&gt;, and &lt;code&gt;PerResolve&lt;/code&gt; work as usual&lt;/li&gt;
&lt;li&gt;collections of cases are assembled through &lt;code&gt;Tag.Unique&lt;/code&gt;; consumers can request &lt;code&gt;IEnumerable&amp;lt;PaymentGateway&amp;gt;&lt;/code&gt;, arrays, &lt;code&gt;ReadOnlySpan&amp;lt;&amp;gt;&lt;/code&gt;, and other supported collection types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/union-types.md" rel="noopener noreferrer"&gt;Union types: case implementations behind a union contract&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Union type support requires a preview language version and .NET 11 Preview 5 or later, but it is already available to try.&lt;/p&gt;




&lt;h3&gt;
  
  
  Generating interfaces from classes
&lt;/h3&gt;

&lt;p&gt;The classic "class plus matching interface" pair is some of the dullest boilerplate in DI-based applications. An interface can now be generated from its implementation: declare an empty &lt;code&gt;partial interface&lt;/code&gt; and mark the class with &lt;code&gt;[GenerateInterface]&lt;/code&gt;. Special thanks to &lt;a href="https://github.com/adamhathcock" rel="noopener noreferrer"&gt;Adam Hathcock&lt;/a&gt; for designing and implementing this feature.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Pure.DI generates the interface body for this empty declaration&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IEmailSender&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;GenerateInterface&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailSender&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEmailSender&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"smtp"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"sent:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// The consumer depends on an abstraction nobody had to write manually&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEmailSender&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The feature goes beyond simple mirroring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/customize-the-generated-interface.md" rel="noopener noreferrer"&gt;interface name, namespace, and accessibility are configurable&lt;/a&gt; through named attribute arguments&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/generate-several-interfaces-from-one-class.md" rel="noopener noreferrer"&gt;one class can generate several interfaces&lt;/a&gt;, with members distributed between contracts by attributes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/ignore-members-in-the-generated-interface.md" rel="noopener noreferrer"&gt;&lt;code&gt;[IgnoreInterface]&lt;/code&gt;&lt;/a&gt; excludes members from the contract&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/generate-interfaces-with-generics.md" rel="noopener noreferrer"&gt;generic members, nullable annotations, and events are preserved&lt;/a&gt;, along with XML documentation comments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/generate-an-interface-from-a-class.md" rel="noopener noreferrer"&gt;Generating an interface from a class&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Implementation-level attribute bindings
&lt;/h3&gt;

&lt;p&gt;Version 2.5.0 redesigned the attribute binding model. This is a breaking change, with migration details below. &lt;code&gt;[Bind]&lt;/code&gt; can now be declared directly on the implementation type and specify its contract, lifetime, and tag. Implementations can describe their own bindings, keeping composition setup concise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;// The setup contains only the root; the attribute defines the binding&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMessageWriter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Writer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"console"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMessageWriter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Lifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"console"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConsoleMessageWriter&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IMessageWriter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attributes inside one &lt;code&gt;[...]&lt;/code&gt; group are merged into a single binding, while separate groups create independent bindings. This is useful when one implementation plays several roles with different contracts, lifetimes, and tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Binding group #1: payment pipeline participant&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IPaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Lifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"audit"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="c1"&gt;// Binding group #2: the same adapter as an audit sink with its own lifetime&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IPaymentAuditSink&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Lifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"operations"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentAuditAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEventStore&lt;/span&gt; &lt;span class="n"&gt;eventStore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;IPaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;IPaymentAuditSink&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition to &lt;code&gt;[Bind]&lt;/code&gt;, the focused &lt;code&gt;[Type]&lt;/code&gt;, &lt;code&gt;[Tag]&lt;/code&gt;, and new &lt;code&gt;[Lifetime]&lt;/code&gt; attributes can be combined. Generic implementations support marker contracts such as &lt;code&gt;typeof(IBox&amp;lt;TT&amp;gt;)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/bind-attribute.md" rel="noopener noreferrer"&gt;BindAttribute on an implementation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/bind-attribute-groups.md" rel="noopener noreferrer"&gt;Bind attribute groups: several roles for one adapter&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;[Export]&lt;/code&gt;: class members as dependency sources
&lt;/h3&gt;

&lt;p&gt;Previously, member-level dependency sources used &lt;code&gt;[Bind]&lt;/code&gt;. That role now belongs to the new &lt;code&gt;[Export]&lt;/code&gt; attribute, making the semantics unambiguous: &lt;code&gt;[Bind]&lt;/code&gt; defines bindings, while &lt;code&gt;[Export]&lt;/code&gt; defines dependency sources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Lifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DeviceFeatureProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PhotoService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPhotoService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"PhotoService"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeviceFeatureProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This property is a dependency source&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Export&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IGps&lt;/span&gt; &lt;span class="n"&gt;Gps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Gps&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Export&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ICamera&lt;/span&gt; &lt;span class="n"&gt;Camera&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Camera&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PhotoService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IGps&lt;/span&gt; &lt;span class="n"&gt;gps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ICamera&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cameraFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPhotoService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;[Export]&lt;/code&gt; accepts optional &lt;code&gt;lifetime&lt;/code&gt; and &lt;code&gt;tags&lt;/code&gt; values, so an exported member behaves like a binding source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GraphicsAdapter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lifetime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"HighPerformance"&lt;/span&gt;&lt;span class="p"&gt;])]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IGpu&lt;/span&gt; &lt;span class="n"&gt;HighPerfGpu&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DiscreteGpu&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RayTracer&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HighPerformance"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;IGpu&lt;/span&gt; &lt;span class="n"&gt;gpu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRenderer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Migration from 2.4.x is straightforward: replace member-level &lt;code&gt;[Bind]&lt;/code&gt; with &lt;code&gt;[Export]&lt;/code&gt;, and replace &lt;code&gt;RootKinds.Exposed&lt;/code&gt; with &lt;code&gt;RootKinds.Exported&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/export-attribute.md" rel="noopener noreferrer"&gt;ExportAttribute: members as dependency sources&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/export-attribute-with-lifetime-and-tag.md" rel="noopener noreferrer"&gt;Export with a lifetime and tag&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Custom binding attributes
&lt;/h3&gt;

&lt;p&gt;If you do not want to annotate implementations with Pure.DI's built-in attributes, declare a &lt;strong&gt;custom&lt;/strong&gt; attribute and register it in the setup. &lt;code&gt;TypeAttribute&amp;lt;T&amp;gt;()&lt;/code&gt;, &lt;code&gt;TagAttribute&amp;lt;T&amp;gt;()&lt;/code&gt;, and the new &lt;code&gt;LifetimeAttribute&amp;lt;T&amp;gt;()&lt;/code&gt; tell the generator which constructor arguments contain the contract, tag, and lifetime. The version below uses &lt;code&gt;Pure.DI.Lifetime&lt;/code&gt;, so the implementation assembly still needs a reference to the Pure.DI API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;// Register the custom attribute as a binding metadata source&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TypeAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ServiceAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TT&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LifetimeAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ServiceAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TT&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TagAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ServiceAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TT&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMessageWriter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Writer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"console"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// The attribute can live with the implementations without depending&lt;/span&gt;
&lt;span class="c1"&gt;// on a particular built-in Pure.DI binding attribute&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;AttributeUsage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AttributeTargets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AllowMultiple&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ServiceAttribute&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Attribute&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ServiceAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Lifetime&lt;/span&gt; &lt;span class="n"&gt;lifetime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Lifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMessageWriter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;Lifetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"console"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConsoleMessageWriter&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IMessageWriter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Custom attributes follow the same group merging rules as the built-in &lt;code&gt;Bind&lt;/code&gt;, &lt;code&gt;Type&lt;/code&gt;, &lt;code&gt;Tag&lt;/code&gt;, and &lt;code&gt;Lifetime&lt;/code&gt; attributes. Duplicate lifetime metadata within one attribute group is reported as an error.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/custom-bind-attribute.md" rel="noopener noreferrer"&gt;Custom binding attribute&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Scopes: &lt;code&gt;SetupScope&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A common requirement is a lifetime tied to a request or operation: a request context, unit of work, or telemetry scope. A new generated method, whose name is configured through &lt;code&gt;ScopeMethodName&lt;/code&gt;, connects a new composition instance to its parent scope. &lt;code&gt;Scoped&lt;/code&gt; instances are unique within the scope and are disposed with it, while a regular &lt;code&gt;Singleton&lt;/code&gt; remains shared.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Request #1: its own scope and RequestContext&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetupScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;checkout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestRoot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// RequestContext is shared within this scope&lt;/span&gt;
    &lt;span class="c1"&gt;// and disposed when the scope ends&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Composition&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Hint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Hint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ScopeMethodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"SetupScope"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;// Per-request lifetime&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Scoped&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IdGenerator&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CheckoutService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ICheckoutService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"RequestRoot"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The method name can match the domain: &lt;code&gt;CreateScope&lt;/code&gt;, &lt;code&gt;BeginRequest&lt;/code&gt;, or &lt;code&gt;OpenSession&lt;/code&gt;. Scopes can also be created through factory methods, and Pure.DI validates that the parent and child scopes are different, preventing accidental self-nesting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/scope-setup-method.md" rel="noopener noreferrer"&gt;Scope setup method: a request scope without a wrapper class&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Platform scopes: Unity scenes and Microsoft DI
&lt;/h3&gt;

&lt;p&gt;The same scope model maps naturally to platform scenarios. In Unity, each loaded scene gets its own scope: &lt;code&gt;Scoped&lt;/code&gt; services are shared within the scene and isolated between scenes, while &lt;code&gt;Singleton&lt;/code&gt; services are shared by the entire application. Unity creates &lt;code&gt;MonoBehaviour&lt;/code&gt; objects itself, and Pure.DI builds them up through builders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Scope&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;ClockConfig&lt;/span&gt; &lt;span class="n"&gt;clockConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Hint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Hint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ScopeMethodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"SetupScope"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;clockConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IClockService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ClockService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IClockSession&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Scoped&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ClockSession&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class="c1"&gt;// Unity creates MonoBehaviour; Pure.DI builds it up&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Builders&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/unity-scene-scopes.md" rel="noopener noreferrer"&gt;Unity scene scopes: one scope per scene&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For integration with &lt;code&gt;Microsoft.Extensions.DependencyInjection&lt;/code&gt;, a composition can implement &lt;code&gt;IServiceScopeFactory&lt;/code&gt; and &lt;code&gt;IServiceScope&lt;/code&gt;. Every &lt;code&gt;composition.CreateScope()&lt;/code&gt; call creates a new scope, and tags can be mapped to keyed services. This model is compatible with Microsoft DI scoped semantics for explicitly declared composition roots. Only roots registered through &lt;code&gt;Root(...)&lt;/code&gt; or &lt;code&gt;RootBind()&lt;/code&gt; can be resolved through &lt;code&gt;IServiceProvider&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/service-provider-with-scope.md" rel="noopener noreferrer"&gt;Service provider with Microsoft DI-compatible scopes&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Nullable reference types end to end
&lt;/h3&gt;

&lt;p&gt;Version 2.4.0 introduced complete support for nullable reference types. Annotations are preserved while reading contracts, building the graph, and generating code. This is a breaking change: code that relied on automatic null checks for nullable arguments may need adjustment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDatabase&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IReportService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReportService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// Nullable arguments no longer receive null checks:&lt;/span&gt;
    &lt;span class="c1"&gt;// null is passed through as intended&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arg&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"defaultTitle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RootArg&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"connectionString"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"connection"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IReportService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"CreateReportService"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;defaultTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reportService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateReportService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReportService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;defaultTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"connection"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;IDatabase&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;optionalDatabase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IReportService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The practical effects are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a non-null binding can satisfy a nullable dependency, which is useful for optional constructor parameters, nullable factory results, and collection elements&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;T?&lt;/code&gt; means that the consumer can handle null, but it does not suppress a graph error when no binding exists&lt;/li&gt;
&lt;li&gt;for generic contracts with nullable arguments, prefer &lt;code&gt;where T : class?&lt;/code&gt; over &lt;code&gt;where T : class&lt;/code&gt; to avoid compiler warnings&lt;/li&gt;
&lt;li&gt;new warnings highlight ambiguous nullable roots in &lt;code&gt;Resolve&lt;/code&gt; methods&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/nullable-reference-types.md" rel="noopener noreferrer"&gt;Nullable reference types: examples and recommendations&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;TryBuildUp&lt;/code&gt;: safe object build-up
&lt;/h3&gt;

&lt;p&gt;Builders complete objects created by external systems such as UI frameworks, serializers, or Unity. Strict &lt;code&gt;BuildUp&lt;/code&gt; throws &lt;code&gt;ArgumentException&lt;/code&gt; when the runtime subtype is unknown to the composition. Pure.DI now generates a safe &lt;code&gt;TryBuildUp&lt;/code&gt; method alongside &lt;code&gt;BuildUp&lt;/code&gt;, avoiding that exception:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlutoniumBattery&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// Generate a builder for every IRobot subtype matching *Bot&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Builders&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IRobot&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"BuildUp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"*Bot"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Known type: build-up succeeds&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cleaner&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuildUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CleanerBot&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Unknown subtype: graceful fallback instead of an exception&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;externalRobot&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ExternalRobot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryBuildUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;externalRobot&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// The object remains untouched; handle the situation here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is especially useful when objects arrive from outside and their concrete runtime type is not known in advance: deserialization, plugins, and Unity scene objects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/builders.md" rel="noopener noreferrer"&gt;Builders and TryBuildUp&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  New C# 13/14 features: partial constructors and &lt;code&gt;OverloadResolutionPriority&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;C# 14 allows a constructor declaration, or contract, to be separated from its body. For example, the contract with DI attributes can live in one part of a partial class while the logic lives in another, possibly generated, part. Pure.DI sees the combined constructor and handles it like a regular one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuditSink&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AuditSinkBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// The defining declaration participates in constructor selection,&lt;/span&gt;
    &lt;span class="c1"&gt;// and parameter attributes are combined with the implementation&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="nf"&gt;AuditSink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"durable"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;IAuditTransport&lt;/span&gt; &lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;AuditSinkOptions&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuditSink&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Place the base(...) or this(...) initializer on the implementation&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="nf"&gt;AuditSink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;IAuditTransport&lt;/span&gt; &lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;AuditSinkOptions&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;BatchSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BatchSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/partial-constructor-injection.md" rel="noopener noreferrer"&gt;Partial constructor injection&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pure.DI also supports &lt;code&gt;OverloadResolutionPriorityAttribute&lt;/code&gt;, introduced in C# 13. The attribute raises or lowers the priority of a constructor overload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;OverloadResolutionPriority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BillingApiClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ResilientHttpOptions&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Keep the legacy constructor for existing callers while newly&lt;/span&gt;
    &lt;span class="c1"&gt;// compiled code, including Pure.DI output, prefers the primary one&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;BillingApiClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LegacyHttpOptions&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ResilientHttpOptions&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rules follow C#: higher values are preferred, unannotated constructors have priority &lt;code&gt;0&lt;/code&gt;, and negative values lower a constructor's priority. &lt;code&gt;[Ordinal]&lt;/code&gt; remains Pure.DI's explicit, stronger selection mechanism. &lt;code&gt;DIW014&lt;/code&gt; concerns a different case, method injection: it warns that a regular generated method call can resolve to another overload with a higher &lt;code&gt;OverloadResolutionPriority&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/overload-resolution-priority.md" rel="noopener noreferrer"&gt;OverloadResolutionPriority in constructor selection&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2. Performance: DI on hot paths
&lt;/h2&gt;

&lt;p&gt;In Pure.DI, "zero overhead" means regular, statically typed code without a runtime container. Release 2.5.1 goes further: dependency injection now works in stack-oriented code with &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code&gt;ref struct&lt;/code&gt; without requiring wrappers or runtime lookup. Potentially unsafe ways of storing stack-only values are diagnosed while the DI graph is built.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code&gt;ReadOnlySpan&amp;lt;T&amp;gt;&lt;/code&gt; as dependencies
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Span&lt;/code&gt; injection works like &lt;code&gt;T[]&lt;/code&gt; injection for immediate use in a constructor or method. For value types, Pure.DI generates &lt;code&gt;stackalloc&lt;/code&gt;, so the dependency collection itself is assembled without a heap allocation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="sc"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="sc"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="sc"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPath&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPath&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Path"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ReadOnlySpan&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPath&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// The span is allocated on the stack and is inexpensive.&lt;/span&gt;
    &lt;span class="c1"&gt;// A ref struct cannot be stored in a field,&lt;/span&gt;
    &lt;span class="c1"&gt;// but it can be processed immediately in the constructor&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;PointCount&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Constructor injection of a &lt;code&gt;ref struct&lt;/code&gt; into a heap type remains available for compatibility but reports &lt;code&gt;DIW012&lt;/code&gt;. Method injection, described below, is the preferred approach for new stack-only code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/span-and-readonlyspan.md" rel="noopener noreferrer"&gt;Span and ReadOnlySpan&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Factories for stack-only values
&lt;/h3&gt;

&lt;p&gt;Starting with C# 13, the standard &lt;code&gt;Func&amp;lt;TArg, TResult&amp;gt;&lt;/code&gt; can accept a &lt;code&gt;ref struct&lt;/code&gt; when its generic parameters are declared with &lt;code&gt;allows ref struct&lt;/code&gt;. Compatible reference assemblies from a modern BCL are also required. Pure.DI provides a safe default binding for &lt;code&gt;Func&amp;lt;ReadOnlySpan&amp;lt;char&amp;gt;, T&amp;gt;&lt;/code&gt;: the span remains local to the synchronous factory invocation and is passed immediately into the object graph being created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReadOnlySpan&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"ParserFactory"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ParserFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsSpan&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Parser&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Ordinal&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ReadOnlySpan&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/default-func-with-readonlyspan.md" rel="noopener noreferrer"&gt;Default Func with ReadOnlySpan&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Custom generic delegates with &lt;code&gt;where T : allows ref struct&lt;/code&gt; are also supported. A stack-only value is passed through &lt;code&gt;ctx.Override(...)&lt;/code&gt; and consumed immediately. In a thread-safe context, the override and injection stay inside &lt;code&gt;lock (ctx.Lock)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;delegate&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;ParserFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;allows&lt;/span&gt; &lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ParserFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReadOnlySpan&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ParserFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReadOnlySpan&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;lock&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Override&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReadOnlySpan&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReadOnlySpan&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Initialized&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ParserFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReadOnlySpan&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"ParserFactory"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;allows ref struct&lt;/code&gt; also works in generic roots and root arguments. &lt;code&gt;Bind&amp;lt;T&amp;gt;()&lt;/code&gt;, &lt;code&gt;RootBind&amp;lt;T&amp;gt;()&lt;/code&gt;, &lt;code&gt;Root&amp;lt;T&amp;gt;()&lt;/code&gt;, and &lt;code&gt;DefaultLifetime&amp;lt;T&amp;gt;()&lt;/code&gt; accept ref-like contracts. &lt;code&gt;Transient&amp;lt;T&amp;gt;()&lt;/code&gt;, &lt;code&gt;PerResolve&amp;lt;T&amp;gt;()&lt;/code&gt;, and &lt;code&gt;PerBlock&amp;lt;T&amp;gt;()&lt;/code&gt; allow ref-like results that remain local while the root is being built. &lt;code&gt;Singleton&amp;lt;T&amp;gt;()&lt;/code&gt; and &lt;code&gt;Scoped&amp;lt;T&amp;gt;()&lt;/code&gt; can accept ref-like dependencies in a parameterized factory, but its stored result must be heap-safe; otherwise Pure.DI reports &lt;code&gt;DIE046&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/allows-ref-struct-factory.md" rel="noopener noreferrer"&gt;Allows ref struct factory&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Zero-copy parsing and method injection on a hot path
&lt;/h3&gt;

&lt;p&gt;When a service has long-lived stable dependencies but receives different data on every call, the per-call value can be passed as a root argument and consumed immediately by an injection method. As a C# 14 bonus, a &lt;code&gt;byte[]&lt;/code&gt; root argument can satisfy a &lt;code&gt;ReadOnlySpan&amp;lt;byte&amp;gt;&lt;/code&gt; injection contract. Pure.DI applies the compiler-provided span conversion, allowing the parser to read the caller-owned array without copying it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMessageRegistry&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MessageRegistry&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPacketHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PacketHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RootArg&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;]&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"frame"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPacketHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Handle"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// The caller retains ownership of the array and can return it&lt;/span&gt;
&lt;span class="c1"&gt;// to a pool after the call completes&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;packet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PacketHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMessageRegistry&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPacketHandler&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Ordinal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ReadOnlySpan&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;messageId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BinaryPrimitives&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadUInt16BigEndian&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Zero-copy packet parsing...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/zero-copy-network-packet-parsing.md" rel="noopener noreferrer"&gt;Zero-copy network packet parsing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the argument itself is stack-only, for example &lt;code&gt;.RootArg&amp;lt;ReadOnlySpan&amp;lt;char&amp;gt;&amp;gt;("path")&lt;/code&gt;, the generated method root gets a &lt;code&gt;scoped&lt;/code&gt; parameter. The compiler guarantees that the value does not outlive the current stack frame. Typical use cases include parsers, routers, protocol decoders, and validation pipelines.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/method-injection-for-a-hot-path.md" rel="noopener noreferrer"&gt;Method injection for a hot path&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Diagnostics for stack-only values
&lt;/h3&gt;

&lt;p&gt;Stack-oriented code makes subtle mistakes easy. Pure.DI moves these failures to compile time with a dedicated set of diagnostics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DIE046&lt;/code&gt; - a stack-only dependency cannot use a stored lifetime such as Singleton or Scoped&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DIE047&lt;/code&gt; - a stack-only dependency cannot be injected into a field or property&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DIE048&lt;/code&gt; - a stack-only implementation cannot be injected through an interface conversion&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DIE049&lt;/code&gt; - a stack-only dependency cannot be captured by a generated delegate or deferred factory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DIW012&lt;/code&gt; - warns about injecting a stack-only dependency into the constructor of a heap type&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DIW013&lt;/code&gt; - warns about an unsynchronized stack-only override in a thread-safe context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of an obscure C# compiler error such as &lt;code&gt;CS9244&lt;/code&gt;, Pure.DI can now report a dedicated diagnostic with an explanation and documentation link.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/DIAGNOSTICS.md" rel="noopener noreferrer"&gt;Pure.DI diagnostics reference&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Non-boxing union results
&lt;/h3&gt;

&lt;p&gt;Returning to union types from a performance perspective, the compact &lt;code&gt;union&lt;/code&gt; representation stores its value as &lt;code&gt;object&lt;/code&gt;, which boxes value-type cases. Hot paths can instead use &lt;strong&gt;custom&lt;/strong&gt; union results that store each value-type case in a dedicated field, avoiding boxing, reflection, and wrapper allocations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;// CacheHit is a value-type case of CacheLookupResult&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CacheLookupResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CacheHit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CacheLookupResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"CachedProduct"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;CachedProduct&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Allocation-free access to the result&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;CacheHit&lt;/span&gt; &lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;CacheHit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ProductId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;CacheMiss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ProductId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// The custom union stores value-type cases directly&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompilerServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;CacheLookupResult&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompilerServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IUnion&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;_kind&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;CacheHit&lt;/span&gt; &lt;span class="n"&gt;_hit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;CacheMiss&lt;/span&gt; &lt;span class="n"&gt;_miss&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CacheLookupResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CacheHit&lt;/span&gt; &lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_hit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_miss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CacheLookupResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CacheMiss&lt;/span&gt; &lt;span class="n"&gt;miss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_hit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_miss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;miss&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;CacheHit&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_hit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_kind&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Value is the required general fallback and boxes a value-type case;&lt;/span&gt;
    &lt;span class="c1"&gt;// use it only for diagnostics&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_kind&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_hit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_miss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/non-boxing-union-result.md" rel="noopener noreferrer"&gt;Non-boxing union result&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  High-performance example catalog
&lt;/h3&gt;

&lt;p&gt;The new capabilities are accompanied by a series of "DI on a hot path" examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/arraypool-buffer.md" rel="noopener noreferrer"&gt;ArrayPool buffers&lt;/a&gt;&lt;/strong&gt; - &lt;code&gt;ArrayPool&amp;lt;T&amp;gt;&lt;/code&gt; is supported out of the box; request &lt;code&gt;ArrayPool&amp;lt;byte&amp;gt;&lt;/code&gt; like any other dependency and return the buffer to the pool when its owner is disposed through &lt;code&gt;Owned&amp;lt;T&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/object-pool.md" rel="noopener noreferrer"&gt;Object pool&lt;/a&gt;&lt;/strong&gt; - a Singleton pool of warm objects plus a short-lived lease for each root call, making reuse explicit and keeping leases within their scope&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/factory-without-closure-capture.md" rel="noopener noreferrer"&gt;Factories without closure capture&lt;/a&gt;&lt;/strong&gt; - dependencies are lambda parameters, such as &lt;code&gt;.To((CurrencyFormatter currency, TaxPolicy tax) =&amp;gt; ...)&lt;/code&gt;, instead of captured values, keeping factory code deterministic and allocation-friendly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/struct-dependency.md" rel="noopener noreferrer"&gt;Struct dependencies&lt;/a&gt;&lt;/strong&gt; - a small value-type dependency is created directly and does not require a separate heap allocation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/valuetask-root.md" rel="noopener noreferrer"&gt;ValueTask roots&lt;/a&gt;&lt;/strong&gt; - a root such as &lt;code&gt;Root&amp;lt;ValueTask&amp;lt;IFeatureSnapshot&amp;gt;&amp;gt;&lt;/code&gt; avoids allocating a &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt; in the synchronous case&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/threadsafe-off-for-single-thread-composition.md" rel="noopener noreferrer"&gt;ThreadSafe = Off&lt;/a&gt;&lt;/strong&gt; - when a composition is created and used on one thread, such as in a CLI tool or game-loop initialization phase, &lt;code&gt;.Hint(Hint.ThreadSafe, "Off")&lt;/code&gt; removes generated synchronization&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Code generation optimizations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;_lock&lt;/code&gt; field is now generated only when at least one &lt;code&gt;lock (...)&lt;/code&gt; statement actually references it; previously every composition reserved space for it&lt;/li&gt;
&lt;li&gt;all generated code is marked with &lt;code&gt;[GeneratedCode]&lt;/code&gt;, allowing analyzers, coverage tools, and code-style tools to recognize and optionally exclude it according to their settings; the generated class also contains the actual Pure.DI package version&lt;/li&gt;
&lt;li&gt;graph construction and generation now use caching, prefer cheaper syntax analysis where it is sufficient, and avoid some repeated graph construction attempts, reducing generator overhead during compilation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Useful extras
&lt;/h2&gt;

&lt;p&gt;A few smaller additions that do not need their own sections:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New factory context properties.&lt;/strong&gt; &lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/root-name.md" rel="noopener noreferrer"&gt;&lt;code&gt;ctx.RootName&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/root-type.md" rel="noopener noreferrer"&gt;&lt;code&gt;ctx.RootType&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/islockrequired.md" rel="noopener noreferrer"&gt;&lt;code&gt;ctx.IsLockRequired&lt;/code&gt;&lt;/a&gt; join &lt;code&gt;ctx.Tag&lt;/code&gt;, &lt;code&gt;ctx.ConsumerType&lt;/code&gt;, and &lt;code&gt;ctx.Lock&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The logger knows which root is constructing it&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RootName&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;// Lock only when it is actually required&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsLockRequired&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;lock&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dictionary injection.&lt;/strong&gt; &lt;code&gt;IReadOnlyDictionary&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt; is assembled automatically from &lt;code&gt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt; bindings marked with &lt;code&gt;Tag.Unique&lt;/code&gt;, making runtime implementation selection by key straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unique&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;EmailChannel&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;KeyValuePair&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INotificationChannel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unique&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;SmsChannel&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;KeyValuePair&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INotificationChannel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IReadOnlyDictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INotificationChannel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/dictionary.md" rel="noopener noreferrer"&gt;Dictionary: select a dependency by key&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More BCL types out of the box.&lt;/strong&gt; &lt;code&gt;TimeProvider&lt;/code&gt;, &lt;code&gt;TaskCompletionSource&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;CultureInfo&lt;/code&gt;, &lt;code&gt;IFormatProvider&lt;/code&gt;, &lt;code&gt;StringComparer&lt;/code&gt;, &lt;code&gt;IReadOnlySet&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;RandomNumberGenerator&lt;/code&gt;, and other types can be injected without additional setup. A &lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/default-bcl-bindings.md" rel="noopener noreferrer"&gt;default binding can be overridden&lt;/a&gt; when necessary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Factories with up to 16 parameters&lt;/strong&gt;, and an update to Roslyn 5.6.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Uno Platform example.&lt;/strong&gt; A cross-platform application using the single-project Uno SDK: a composition exposed as a XAML resource, virtual roots for view models, and &lt;code&gt;DesignTimeComposition&lt;/code&gt; with override roots for the designer, all without a runtime container. &lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/UnoApp.md" rel="noopener noreferrer"&gt;Read the detailed example&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;To try the new features, start with &lt;a href="https://github.com/DevTeam/Pure.DI?tab=readme-ov-file#examples" rel="noopener noreferrer"&gt;any example&lt;/a&gt;. Each example is independent and easy to run with &lt;code&gt;dotnet run&lt;/code&gt;. If something is missing, feel free to open an issue in the &lt;a href="https://github.com/DevTeam/Pure.DI" rel="noopener noreferrer"&gt;Pure.DI GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for your interest and for reading to the end!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Pure.DI: DI without container, without .NET type reflection, and with compile-time validation</title>
      <dc:creator>Nikolay Pianikov</dc:creator>
      <pubDate>Tue, 17 Mar 2026 10:48:32 +0000</pubDate>
      <link>https://dev.to/nikolayp/puredi-di-without-container-without-net-type-reflection-and-with-compile-time-validation-284k</link>
      <guid>https://dev.to/nikolayp/puredi-di-without-container-without-net-type-reflection-and-with-compile-time-validation-284k</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbmue2d853wavglfkqdc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbmue2d853wavglfkqdc.gif" alt="Pure.DI" width="760" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pure.DI is a &lt;strong&gt;C# code generator for dependency injection&lt;/strong&gt; that builds the dependency graph &lt;strong&gt;at compile time&lt;/strong&gt; and generates ordinary C# code for object creation. As a result, you get "pure DI": no service locator, no reflection, and problems detected at compile time.&lt;/p&gt;

&lt;p&gt;This article covers the basic capabilities of Pure.DI. It is intended for developers who write in C#, have already encountered DI containers, and want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;improve DI predictability and performance;&lt;/li&gt;
&lt;li&gt;see the real object graph instead of a "magical" runtime container;&lt;/li&gt;
&lt;li&gt;detect DI configuration errors at compile time;&lt;/li&gt;
&lt;li&gt;use DI in environments where .NET Reflection is undesirable (AOT, Unity, older frameworks, library projects).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Details: &lt;a href="https://github.com/DevTeam/Pure.DI" rel="noopener noreferrer"&gt;Pure.DI README&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  DI: what we are actually trying to solve
&lt;/h2&gt;

&lt;p&gt;Imagine a typical application scenario: a service processes an order, logs events, and interacts with an external payment gateway.&lt;/p&gt;

&lt;p&gt;If dependencies are created "inside" services, the code quickly turns into a tangle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bad: the service itself determines what and how to create&lt;/span&gt;
&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CheckoutService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;_http&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;PaymentGatewayClient&lt;/span&gt; &lt;span class="n"&gt;_gateway&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CheckoutService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_gateway&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PaymentGatewayClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"hardcoded"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;CheckoutAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PayAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problems here are standard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt;: difficult to replace &lt;code&gt;PaymentGatewayClient&lt;/code&gt; and &lt;code&gt;HttpClient&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration&lt;/strong&gt;: API keys and settings end up inside business code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifetime&lt;/strong&gt;: who and when should release resources?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tight coupling&lt;/strong&gt;: domain logic starts to depend on infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DI is just a tool: &lt;em&gt;an object should not create its dependencies; they are passed to it from the outside&lt;/em&gt;. Most often — through the constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CheckoutService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IPaymentGateway&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;CheckoutAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PayAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The question arises: &lt;strong&gt;who will create &lt;code&gt;IPaymentGateway&lt;/code&gt;, &lt;code&gt;ILogger&lt;/code&gt;, and &lt;code&gt;CheckoutService&lt;/code&gt; itself?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer is &lt;em&gt;composition&lt;/em&gt; of the application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pure DI: "no container", but dependency injection exists
&lt;/h2&gt;

&lt;p&gt;In the classic approach, you configure a DI container, and then at runtime the container builds the dependency graph and provides objects on request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pure DI&lt;/strong&gt; — an approach where there is no container as a runtime entity. There are only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;object composition&lt;/strong&gt; (how to assemble the graph into a concrete object),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;composition roots&lt;/strong&gt; — entry points from which the needed composition is built.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In an ideal world, you want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;object creation to be done by &lt;strong&gt;ordinary code&lt;/strong&gt; (without reflection and dynamic calls),&lt;/li&gt;
&lt;li&gt;object composition to be &lt;strong&gt;transparent&lt;/strong&gt; and debuggable,&lt;/li&gt;
&lt;li&gt;errors like "missing dependency" or "cycle in graph" to be detected &lt;strong&gt;before production&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is exactly what Pure.DI does.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Pure.DI — in simple terms
&lt;/h2&gt;

&lt;p&gt;Pure.DI is a &lt;strong&gt;compile-time DI code generator&lt;/strong&gt;: you define the dependency graph (bindings, tags, lifetimes, roots), and the generator:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;analyzes this dependency graph at compile time;&lt;/li&gt;
&lt;li&gt;verifies that the graph is correct (no "holes", cycles, inaccessible constructors, etc.);&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;generates&lt;/strong&gt; a partial composition class with ordinary properties/methods that create object compositions, with roots as "initial" objects of the composition.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The obvious key advantages of this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Overhead&lt;/strong&gt;: at runtime there is no container, no assembly scanning, no type reflection; exactly what you would write by hand is created — a chain of &lt;code&gt;new&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compile-Time Validation&lt;/strong&gt;: DI configuration errors become compilation errors/warnings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works everywhere&lt;/strong&gt;: no runtime dependencies — can be used even in .NET Framework 2.0+, or in AOT/Unity scenarios.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparency&lt;/strong&gt;: you can view and debug the generated code just like your regular code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in BCL Support&lt;/strong&gt;: many types from .NET BCL (&lt;code&gt;Func&amp;lt;&amp;gt;&lt;/code&gt;, &lt;code&gt;Lazy&amp;lt;&amp;gt;&lt;/code&gt;, &lt;code&gt;IEnumerable&amp;lt;&amp;gt;&lt;/code&gt;, &lt;code&gt;Task&lt;/code&gt;, &lt;code&gt;ValueTask&lt;/code&gt;, &lt;code&gt;Span&lt;/code&gt;, &lt;code&gt;Tuple&lt;/code&gt;, etc.) are supported "out of the box".&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why this is better than a "regular" DI container in real work
&lt;/h2&gt;

&lt;p&gt;Below are only practical advantages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Predictable performance
&lt;/h3&gt;

&lt;p&gt;If DI is reduced to generated code &lt;code&gt;new A(new B(new C()))&lt;/code&gt;, then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no costs for reflection/dynamics;&lt;/li&gt;
&lt;li&gt;no hidden allocations for graph construction;&lt;/li&gt;
&lt;li&gt;no other "magic", which simplifies profiling and optimization.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Errors — at compile time, not at runtime
&lt;/h3&gt;

&lt;p&gt;In a classic container, you may not notice a registration error for a long time, until the execution path (possibly very rare) leads to a problematic object graph. Pure.DI builds the graph in advance — and immediately reports problems. Problematic code simply won't compile, no matter how hard you try.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clear composition roots instead of endless &lt;code&gt;Resolve&amp;lt;T&amp;gt;()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In Pure.DI, composition roots are &lt;strong&gt;explicit&lt;/strong&gt; properties or methods. This disciplines the architecture: you know exactly what objects your "container" provides externally. No Service Locator.&lt;/p&gt;

&lt;h3&gt;
  
  
  Convenient for libraries and limited environments
&lt;/h3&gt;

&lt;p&gt;If you are writing a library, module, plugin, Unity code, or an AOT application — the absence of runtime dependencies and reflection often becomes a decisive factor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;

&lt;p&gt;Before starting, it's useful to know two technical requirements (and it's important to understand that they relate to the generator, not your application):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;.NET SDK 6.0.4+&lt;/strong&gt; is required for compilation (however, projects can target older platforms, down to .NET Framework 2.0+);&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C# 8+&lt;/strong&gt; is required only for projects where the Pure.DI source generator is enabled (other projects in the solution can be on any version of C#).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What packages exist (most often you only need the first one):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.nuget.org/packages/Pure.DI" rel="noopener noreferrer"&gt;Pure.DI&lt;/a&gt; — DI code generator;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.nuget.org/packages/Pure.DI.Abstractions" rel="noopener noreferrer"&gt;Pure.DI.Abstractions&lt;/a&gt; — common abstractions/attributes;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.nuget.org/packages/Pure.DI.MS" rel="noopener noreferrer"&gt;Pure.DI.MS&lt;/a&gt; — additions for integration with Microsoft DI;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.nuget.org/packages/Pure.DI.Templates" rel="noopener noreferrer"&gt;Pure.DI.Templates&lt;/a&gt; — templates for creating projects from the command line.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Minimal example: there is a service that sends emails, and we want to create it through DI.&lt;/p&gt;

&lt;p&gt;1) Add the package:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Pure.DI" rel="noopener noreferrer"&gt;Pure.DI on NuGet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) Describe bindings and composition root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Pure.DI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IClock&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SystemClock&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SmtpEmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;INotificationService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// Composition root: public entry point into the graph&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;INotificationService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Notifications"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendWelcome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dev@company.com"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IClock&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt; &lt;span class="n"&gt;Now&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SystemClock&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IClock&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt; &lt;span class="n"&gt;Now&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IEmailSender&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SmtpEmailSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IClock&lt;/span&gt; &lt;span class="n"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEmailSender&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Real sending would be here&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;] -&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;INotificationService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SendWelcome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEmailSender&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;INotificationService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SendWelcome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Welcome!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"We are glad to see you in the system."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this step, the generator will create a partial class &lt;code&gt;Composition&lt;/code&gt; and a property &lt;code&gt;Notifications&lt;/code&gt; that returns the assembled graph. It's very simple.&lt;/p&gt;

&lt;p&gt;See also real examples in the repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI?tab=readme-ov-file#schr%C3%B6dingers-cat-demonstrates-how-it-all-works-" rel="noopener noreferrer"&gt;Example with Schrödinger's cat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/injections-of-abstractions.md" rel="noopener noreferrer"&gt;Example of binding abstractions to implementations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/auto-bindings.md" rel="noopener noreferrer"&gt;Example of automatic binding (auto‑bindings)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/composition-roots.md" rel="noopener noreferrer"&gt;How composition roots work&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Composition class
&lt;/h2&gt;

&lt;p&gt;From an architectural perspective, &lt;code&gt;Composition&lt;/code&gt; is the place where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;bindings&lt;/strong&gt; are defined: "use &lt;code&gt;SmtpEmailSender&lt;/code&gt; instead of &lt;code&gt;IEmailSender&lt;/code&gt;";&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;roots&lt;/strong&gt; are defined: "externally we expose only &lt;code&gt;INotificationService Notifications&lt;/code&gt;";&lt;/li&gt;
&lt;li&gt;dependency &lt;strong&gt;lifetimes&lt;/strong&gt; are configured;&lt;/li&gt;
&lt;li&gt;(optionally) &lt;strong&gt;hints&lt;/strong&gt; are set for the generator.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An important point: Pure.DI does not "hide" somewhere at runtime. A &lt;strong&gt;regular class&lt;/strong&gt; appears in the project that can be opened, debugged, and studied in detail.&lt;/p&gt;

&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/resolve-methods.md" rel="noopener noreferrer"&gt;Example of composition roots and Resolve methods&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/resolve-hint.md" rel="noopener noreferrer"&gt;How to disable Resolve method generation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Bindings: how to link interfaces with implementations
&lt;/h2&gt;

&lt;p&gt;Basic binding form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IContract&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Lifetime&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Implementation&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is exactly what you're used to seeing in DI containers, but the result will be generated code.&lt;/p&gt;

&lt;p&gt;Real scenario: two delivery methods — courier and parcel locker. Business code depends on &lt;code&gt;IDeliveryService&lt;/code&gt;, and the specific implementation is chosen at the composition level - infrastructure specialized for creating objects of specific types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Pure.DI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDeliveryService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CourierDelivery&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IOrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IOrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Orders"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PlaceOrder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IDeliveryService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Ship&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CourierDelivery&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDeliveryService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Ship&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A courier is on his way"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IOrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;PlaceOrder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IDeliveryService&lt;/span&gt; &lt;span class="n"&gt;delivery&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IOrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;PlaceOrder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// business logic...&lt;/span&gt;
        &lt;span class="n"&gt;delivery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ship&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/injections-of-abstractions.md" rel="noopener noreferrer"&gt;Example of binding abstractions to implementations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Automatic bindings: convenient, but be vigilant
&lt;/h2&gt;

&lt;p&gt;Pure.DI can create &lt;strong&gt;non-abstract&lt;/strong&gt; types without explicit bindings. This is convenient for small applications, utilities, and demos: you declare a root, and dependencies are "pulled" automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Pure.DI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReportService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Reports"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reports&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileSystem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReportService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileSystem&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, in a real application, automatic binding quickly hits architectural limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;harder to follow the dependency inversion principle (you start depending on concrete classes);&lt;/li&gt;
&lt;li&gt;harder to manage different implementations of the same abstractions and decorators.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, the typical recommendation is to &lt;strong&gt;depend on abstractions&lt;/strong&gt; and explicitly bind them to implementations.&lt;/p&gt;

&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/auto-bindings.md" rel="noopener noreferrer"&gt;Example of automatic binding&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Factories: when you need more than just calling a constructor
&lt;/h2&gt;

&lt;p&gt;Sometimes an object cannot be created only through a constructor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;manual initialization is needed (connect to DB, warm up cache, assemble config);&lt;/li&gt;
&lt;li&gt;object is created through a third-party API;&lt;/li&gt;
&lt;li&gt;additional verification/configuration is needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Pure.DI, there is a binding to a factory for this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Pure.DI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDbConnection&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DbConnection&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Inject() creates DbConnection with all dependencies (if any)&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;DbConnection&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Repo"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IDbConnection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DbConnection&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDbConnection&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IDbConnection&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if the factory is simple, you can describe it briefly — lambda parameters will be injected automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Pure.DI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogFileName&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;To&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;DateTimeOffset&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LogFileName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"app-&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;yyyy&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;MM&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dd&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.log"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogFileName&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"LogName"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LogName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ILogFileName&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;LogFileName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ILogFileName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/factory.md" rel="noopener noreferrer"&gt;Factory with manual initialization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/simplified-factory.md" rel="noopener noreferrer"&gt;Simplified factory (dependencies as lambda parameters)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Simplified bindings: when the contract can be determined automatically
&lt;/h2&gt;

&lt;p&gt;In large projects, the routine is not DI as a concept, but the number of lines of its configuration. Pure.DI allows simplifying the records.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Bind().To&amp;lt;Implementation&amp;gt;()&lt;/code&gt; binds &lt;strong&gt;the type itself&lt;/strong&gt; and its &lt;strong&gt;non-special&lt;/strong&gt; abstractions that it directly implements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SmtpEmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sender"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IEmailSender&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SmtpEmailSender&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEmailSender&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful when a class implements one or more "regular" contracts, and you don't want to list them manually.&lt;/p&gt;

&lt;p&gt;There are variants of "binding by lifetime" through methods &lt;code&gt;Singleton&amp;lt;&amp;gt;()&lt;/code&gt;, &lt;code&gt;PerResolve&amp;lt;&amp;gt;()&lt;/code&gt;, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SystemClock&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sender"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ISystemClock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SystemClock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ISystemClock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IEmailSender&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ISystemClock&lt;/span&gt; &lt;span class="n"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;IEmailSender&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/simplified-binding.md" rel="noopener noreferrer"&gt;Simplified bindings without specifying a contract&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/simplified-lifetime-specific-bindings.md" rel="noopener noreferrer"&gt;Short binding methods with lifetime in the name&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Tags: multiple implementations of one contract
&lt;/h2&gt;

&lt;p&gt;In a real application, there are often several implementations of one interface:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;different logging methods (file, console, telemetry);&lt;/li&gt;
&lt;li&gt;different API clients (public/internal);&lt;/li&gt;
&lt;li&gt;different payment providers (credit card/bank transfer/gift certificates).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tags allow you to select an implementation &lt;strong&gt;explicitly&lt;/strong&gt; without creating additional interfaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Pure.DI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPaymentClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sandbox"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SandboxPaymentClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPaymentClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Prod"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProdPaymentClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CheckoutService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CheckoutService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CheckoutService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Checkout"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Checkout&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IPaymentClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SandboxPaymentClient&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPaymentClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProdPaymentClient&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPaymentClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CheckoutService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Prod"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;IPaymentClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/tags.md" rel="noopener noreferrer"&gt;Example of using tags&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/smart-tags.md" rel="noopener noreferrer"&gt;Smart tags (without strings and typos)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Lifetimes
&lt;/h2&gt;

&lt;p&gt;Pure.DI supports familiar lifetimes. Important: this is not a "DI feature", but a tool for resource management and state isolation.&lt;/p&gt;

&lt;p&gt;Below is a simplified cheat sheet (in terms of behavior):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lifetime&lt;/th&gt;
&lt;th&gt;When created&lt;/th&gt;
&lt;th&gt;When the same instance is reused&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Transient&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;from scratch each time&lt;/td&gt;
&lt;td&gt;never&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Singleton&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;once per &lt;code&gt;Composition&lt;/code&gt; instance&lt;/td&gt;
&lt;td&gt;everywhere within a single composition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PerResolve&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;on each access to a root&lt;/td&gt;
&lt;td&gt;within one root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PerBlock&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;inside a construction block&lt;/td&gt;
&lt;td&gt;allows reducing the number of instances (details depend on the graph)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Scoped&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;on a scope-composition&lt;/td&gt;
&lt;td&gt;within one scope&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Practical guideline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Transient&lt;/code&gt; — safe default for most stateless services.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Singleton&lt;/code&gt; — for caches/pools/metadata, but requires &lt;strong&gt;thread safety&lt;/strong&gt; and carefulness.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Scoped&lt;/code&gt; — for "request resources": DbContext/UnitOfWork/RequestTelemetry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/transient.md" rel="noopener noreferrer"&gt;Transient: new object each time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/singleton.md" rel="noopener noreferrer"&gt;Singleton: one object per Composition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/perresolve.md" rel="noopener noreferrer"&gt;PerResolve: one object per root&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/perblock.md" rel="noopener noreferrer"&gt;PerBlock: reduces number of instances&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/scope.md" rel="noopener noreferrer"&gt;Scope/Scoped: "per request"&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Composition arguments: how to pass external state without global static variables
&lt;/h2&gt;

&lt;p&gt;DI combines poorly with passing additional external state (some data) to the created object. But in Pure.DI, &lt;strong&gt;Composition arguments&lt;/strong&gt; turn external state into dependencies available in the graph like any others:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Pure.DI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IApiClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApiClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IApiClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Api"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arg&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"baseUrl"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arg&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"api token"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://api.company.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"secret"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Api&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IApiClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api token"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IApiClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/composition-arguments.md" rel="noopener noreferrer"&gt;Example of composition arguments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Root arguments: when parameters are needed only at one entry point
&lt;/h2&gt;

&lt;p&gt;Sometimes values should be passed &lt;strong&gt;not to Composition&lt;/strong&gt;, but to a specific root: for example, a command handler receives &lt;code&gt;userId&lt;/code&gt;, and the rest of the composition does not depend on it.&lt;/p&gt;

&lt;p&gt;For this, there are &lt;strong&gt;Root arguments&lt;/strong&gt;. Such a root becomes a method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Pure.DI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;// RootArg is incompatible with Resolve methods (better to disable)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Hint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Hint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Off"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RootArg&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"CreateUserService"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateUserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IUserService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IUserService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/root-arguments.md" rel="noopener noreferrer"&gt;Example of root arguments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Generation and usage: roots as properties (and why this is convenient)
&lt;/h2&gt;

&lt;p&gt;A key feature of Pure.DI — composition roots become &lt;strong&gt;regular&lt;/strong&gt; class members, properties or methods.&lt;/p&gt;

&lt;p&gt;This changes the usage style:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the root is easy to substitute in UI-binding (WPF/MAUI/Avalonia), because it's a property/method;&lt;/li&gt;
&lt;li&gt;IDE and compiler help with navigation;&lt;/li&gt;
&lt;li&gt;easy to document;&lt;/li&gt;
&lt;li&gt;a dependency only participates in composition if you explicitly declared a root for it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If desired, you can resolve dependencies through &lt;code&gt;Resolve&lt;/code&gt;, but it's better to perceive this as "familiar, but not always rational approach".&lt;/p&gt;

&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/resolve-methods.md" rel="noopener noreferrer"&gt;Resolve methods and their limitations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Resolve methods: convenient, but it's Service Locator
&lt;/h2&gt;

&lt;p&gt;Pure.DI can generate &lt;code&gt;Resolve&amp;lt;T&amp;gt;()&lt;/code&gt; and &lt;code&gt;Resolve(Type)&lt;/code&gt; methods — this is sometimes useful, for example, for integration with code that prefers to work with classic DI containers. But essentially this is &lt;strong&gt;Service Locator&lt;/strong&gt;, with all the classic drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API allows "getting anything" — this is loss of control;&lt;/li&gt;
&lt;li&gt;there is a risk of runtime exceptions, for example, when there is no corresponding binding to a specific (non-abstract) type;&lt;/li&gt;
&lt;li&gt;code becomes harder to analyze and test.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want a strict and clean architecture, &lt;code&gt;Resolve&lt;/code&gt; is usually disabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Pure.DI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Hint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Hint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Off"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"App"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/resolve-hint.md" rel="noopener noreferrer"&gt;Resolve hint (enable/disable)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Dependency injection methods
&lt;/h2&gt;

&lt;p&gt;In most projects, &lt;strong&gt;constructor injection&lt;/strong&gt; is sufficient — it's simpler, safer, and well-supported by analyzers/IDE.&lt;/p&gt;

&lt;p&gt;But sometimes other options are convenient (for example, when "building up" an object created externally).&lt;/p&gt;

&lt;p&gt;Pure.DI supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;injection through constructor;&lt;/li&gt;
&lt;li&gt;injection through properties;&lt;/li&gt;
&lt;li&gt;injection through fields;&lt;/li&gt;
&lt;li&gt;injection through methods.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Properties, fields, and methods just need to be marked with the &lt;code&gt;[Dependency]&lt;/code&gt; attribute or others like &lt;code&gt;[Ordinal]&lt;/code&gt;, &lt;code&gt;[Tag]&lt;/code&gt;, &lt;code&gt;[Type]&lt;/code&gt;, &lt;code&gt;[Inject]&lt;/code&gt;. But you can always easily extend them with your own attributes, thereby making your code completely independent of DI.&lt;/p&gt;

&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/property-injection.md" rel="noopener noreferrer"&gt;Injection through properties&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/field-injection.md" rel="noopener noreferrer"&gt;Injection through fields&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/method-injection.md" rel="noopener noreferrer"&gt;Injection through methods&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Builders (BuildUp): when it's not possible to control object creation
&lt;/h2&gt;

&lt;p&gt;Sometimes an object appears "from outside":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deserialization (JSON → object);&lt;/li&gt;
&lt;li&gt;plugins/scripts;&lt;/li&gt;
&lt;li&gt;game entities created by the engine;&lt;/li&gt;
&lt;li&gt;UI elements created by the framework.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In such cases, the &lt;strong&gt;BuildUp&lt;/strong&gt; pattern is useful: you already have an instance, and you want to "add" dependencies to it through fields/properties/methods marked with injection attributes mentioned above.&lt;/p&gt;

&lt;p&gt;Pure.DI can generate &lt;strong&gt;builders&lt;/strong&gt; for types derived from a base &lt;code&gt;T&lt;/code&gt;, known at compile time.&lt;/p&gt;

&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/builders.md" rel="noopener noreferrer"&gt;Builders: building up existing objects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/builder-with-arguments.md" rel="noopener noreferrer"&gt;Builder with arguments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Generics: why Pure.DI offers marker types instead of "open generics"
&lt;/h2&gt;

&lt;p&gt;Classic DI containers often register "open generics" like &lt;code&gt;IRepository&amp;lt;&amp;gt; → Repository&amp;lt;&amp;gt;&lt;/code&gt;. This is convenient, but in complex graphs, ambiguity arises: how exactly to match type arguments, especially when interfaces and implementations use different orders or names of parameters.&lt;/p&gt;

&lt;p&gt;In Pure.DI, instead of "open generics", an approach with &lt;strong&gt;marker types&lt;/strong&gt; (for example, &lt;code&gt;TT&lt;/code&gt;, &lt;code&gt;TT1&lt;/code&gt;, &lt;code&gt;TT2&lt;/code&gt;) is used. This makes matching absolutely &lt;strong&gt;precise&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In practice, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Pure.DI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TT&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TT&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDataService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DataService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDataService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Data"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IDataService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDataService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/generics.md" rel="noopener noreferrer"&gt;Generics and marker types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/complex-generics.md" rel="noopener noreferrer"&gt;Complex generics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  On-demand injection: Func, Lazy, and factories
&lt;/h2&gt;

&lt;p&gt;Sometimes a service needs to create dependencies &lt;strong&gt;not immediately&lt;/strong&gt;, but as needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a "heavy" dependency (driver initialization, data warming);&lt;/li&gt;
&lt;li&gt;many instances of one type (list items, game entities);&lt;/li&gt;
&lt;li&gt;dependencies with runtime parameters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pure.DI supports factory delegates (&lt;code&gt;Func&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;Func&amp;lt;TArg, T&amp;gt;&lt;/code&gt;, &lt;code&gt;Func&amp;lt;TArg1, TArg2, ..., T&amp;gt;&lt;/code&gt;) and &lt;code&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt; out of the box.&lt;/p&gt;

&lt;p&gt;Example: a service generates several one-time tokens, each time creating a new object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Pure.DI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ITokenGenerator&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TokenGenerator&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ITokenService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TokenService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ITokenService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tokens"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IssueBatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ITokenGenerator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TokenGenerator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ITokenGenerator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ITokenService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;IssueBatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TokenService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ITokenGenerator&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;generatorFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ITokenService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;IssueBatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gen&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generatorFactory&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// new instance on demand&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/injection-on-demand.md" rel="noopener noreferrer"&gt;Injection on demand (Func)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/injections-on-demand-with-arguments.md" rel="noopener noreferrer"&gt;Injection on demand with arguments (Func)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/func.md" rel="noopener noreferrer"&gt;BCL: Func&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/lazy.md" rel="noopener noreferrer"&gt;BCL: Lazy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  BCL support: when standard types "just work"
&lt;/h2&gt;

&lt;p&gt;In traditional containers, many tasks are solved by special extensions. In Pure.DI, a significant part of useful types from the .NET Base Class Library (BCL) is supported "out of the box".&lt;/p&gt;

&lt;p&gt;From frequently used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Func&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;Func&amp;lt;TArg1, T&amp;gt;&lt;/code&gt;, &lt;code&gt;Func&amp;lt;TArg1, TArg2, ..., T&amp;gt;&lt;/code&gt; — factories;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt; — lazy creation;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;, arrays — collections of dependencies;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;ValueTask&amp;lt;T&amp;gt;&lt;/code&gt; — for async roots;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IServiceProvider&lt;/code&gt;/Service collection scenarios (when integration with the Microsoft DI ecosystem is needed).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/enumerable.md" rel="noopener noreferrer"&gt;BCL: Enumerable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/enumerable-generics.md" rel="noopener noreferrer"&gt;BCL: Enumerable generics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/task.md" rel="noopener noreferrer"&gt;BCL: Task&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/valuetask.md" rel="noopener noreferrer"&gt;BCL: ValueTask&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/service-provider.md" rel="noopener noreferrer"&gt;BCL: Service provider&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/service-collection.md" rel="noopener noreferrer"&gt;BCL: Service collection&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Decorators and interception: adding logging and metrics without rewriting code
&lt;/h2&gt;

&lt;p&gt;Interception in Pure.DI in the basic variant works well with the &lt;strong&gt;Decorator&lt;/strong&gt; pattern: we "wrap" the implementation in another implementation of the same interface.&lt;/p&gt;

&lt;p&gt;Real example: wrap &lt;code&gt;IOrderService&lt;/code&gt; in a logging decorator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Pure.DI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;DI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;// "base" — base implementation&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// Decorator uses "base"&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IOrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoggingOrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IOrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Orders"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Composition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PlaceOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ORD-42"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ILogger&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConsoleLogger&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IOrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;PlaceOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IOrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;PlaceOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"The order &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; has been placed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoggingOrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ILogger&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;IOrderService&lt;/span&gt; &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IOrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;PlaceOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Starting the registration process &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PlaceOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"The registration has been completed &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/decorator.md" rel="noopener noreferrer"&gt;Decorator: basic interception scenario&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/interception.md" rel="noopener noreferrer"&gt;Interception: additional capabilities&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Hints: additional generation settings that help during development
&lt;/h2&gt;

&lt;p&gt;Hints are fine-tuned generator settings of "how exactly to generate code". For basic startup, it's enough to know three:&lt;/p&gt;

&lt;h3&gt;
  
  
  ToString: see the dependency graph
&lt;/h3&gt;

&lt;p&gt;You can enable the generation of &lt;code&gt;ToString()&lt;/code&gt;, which returns a diagram in mermaid format — convenient for review and discussing architecture.&lt;/p&gt;

&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/tostring-hint.md" rel="noopener noreferrer"&gt;Hint ToString: graph diagram&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ThreadSafe: disable thread safety if you're sure
&lt;/h3&gt;

&lt;p&gt;By default, generation takes multithreading into account. But sometimes object composition is built strictly in one thread (for example, when starting the application), and you can get slightly higher performance.&lt;/p&gt;

&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/threadsafe-hint.md" rel="noopener noreferrer"&gt;Hint ThreadSafe&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  OnDependencyInjection: point for "dynamic interception"
&lt;/h3&gt;

&lt;p&gt;If it's necessary to centrally "track" or modify the injection process (logging, metrics, control), you can enable the generation of the partial method &lt;code&gt;OnDependencyInjection&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/ondependencyinjection-wildcard-hint.md" rel="noopener noreferrer"&gt;OnDependencyInjection (wildcard)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DevTeam/Pure.DI/blob/master/readme/ondependencyinjection-regular-expression-hint.md" rel="noopener noreferrer"&gt;OnDependencyInjection (regexp)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Practical recommendations for implementing Pure.DI in a project
&lt;/h2&gt;

&lt;p&gt;For successful implementation, the following sequence is recommended:&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;Define composition roots&lt;/strong&gt;. Usually these are application "entry points": &lt;code&gt;App&lt;/code&gt;, &lt;code&gt;MainController&lt;/code&gt;, &lt;code&gt;MessageHandler&lt;/code&gt;, &lt;code&gt;BackgroundWorker&lt;/code&gt;. Minimize their number, ideally to one root.&lt;br&gt;
2) &lt;strong&gt;Reduce object creation to composition&lt;/strong&gt;. In other modules, let only business code and contracts remain. The fewer objects are created manually, the better.&lt;br&gt;
3) &lt;strong&gt;Start with constructor injection&lt;/strong&gt;. Use other types of injection as a tool for build-up and integration.&lt;br&gt;
4) &lt;strong&gt;Use auto-binding with caution&lt;/strong&gt;: excellent for demos, often unnecessary for good architecture.&lt;br&gt;
5) &lt;strong&gt;Don't abuse Singleton&lt;/strong&gt;. And if it's necessary — ensure thread safety and correct resource release. Remember about dependency capture - if a Singleton uses some dependency with a different lifetime, that dependency will also be a Singleton.&lt;br&gt;
6) &lt;strong&gt;Don't make using &lt;code&gt;Resolve()&lt;/code&gt; methods the main model&lt;/strong&gt;. Composition roots are your path to clean architecture and calm updates in production environment.&lt;br&gt;
7) &lt;strong&gt;Keep constructors simple&lt;/strong&gt;: without heavy logic and I/O operations. This will allow you not to worry about composition size and not force you to use various tricks like delayed object creation. If necessary — use factories or separate initialization logic.&lt;br&gt;
8) &lt;strong&gt;Don't get carried away with factories&lt;/strong&gt;: factories are always additional logic that requires support. Use regular bindings to implementations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Pure.DI makes DI predictable and transparent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the compiler guarantees the correctness of object composition;&lt;/li&gt;
&lt;li&gt;object creation turns into readable code;&lt;/li&gt;
&lt;li&gt;lifetimes and tags are described declaratively;&lt;/li&gt;
&lt;li&gt;the application remains as fast and predictable as possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the idea of "Pure DI" is close to you, not "runtime DI container", Pure.DI is worth trying on at least one service or module — usually after that, it's hard to return to the "black DI box".&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>dependencyinversion</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
