Pure.DI by Nikolay Pianikov
Nuget / site data
Details
Info
Name: Pure.DI
Author: Nikolay Pianikov
NuGet: https://www.nuget.org/packages/Pure.DI/
You can find more details at https://github.com/DevTeam/Pure.DI
Source : https://github.com/DevTeam/Pure.DI
Original Readme
Pure DI for .NET

Supports .NET starting with .NET Framework 2.0, released 2005-10-27, and all newer versions.
Usage requirements
.NET SDK 6.0.4 or later is installed. At the same time, you can develop .NET projects even for older versions like .NET Framework 2.0
C# 8 or later. This requirement only needs to be met for projects that reference the Pure.DI source code generator, other projects can use any version of C#.
Key features
Pure.DI is not a framework or library, but a source code generator for creating object graphs. To make them accurate, the developer uses a set of intuitive hints from the Pure.DI API. During the compilation phase, Pure.DI determines the optimal graph structure, checks its correctness, and generates partial class code to create object graphs in the Pure DI paradigm using only basic language constructs. The resulting generated code is robust, works everywhere, throws no exceptions, does not depend on .NET library calls or .NET reflections, is efficient in terms of performance and memory consumption, and is subject to all optimizations. This code can be easily integrated into an application because it does not use unnecessary delegates, additional calls to any methods, type conversions, boxing/unboxing, etc.
- DI without any IoC/DI containers, frameworks, dependencies and hence no performance impact or side effects.
Pure.DI is actually a .NET code generator. It uses basic language constructs to create simple code as well as if you were doing it yourself: de facto it's just a bunch of nested constructor calls. This code can be viewed, analyzed at any time, and debugged.
- A predictable and verified dependency graph is built and validated on the fly while writing code.
All logic for analyzing the graph of objects, constructors and methods takes place at compile time. Pure.DI notifies the developer at compile time of missing or cyclic dependencies, cases when some dependencies are not suitable for injection, etc. The developer has no chance to get a program that will crash at runtime because of some exception related to incorrect object graph construction. All this magic happens at the same time as the code is written, so you have instant feedback between the fact that you have made changes to your code and the fact that your code is already tested and ready to use.
- Does not add any dependencies to other assemblies.
When using pure DI, no dependencies are added to assemblies because only basic language constructs and nothing more are used.
- Highest performance, including compiler and JIT optimization and minimal memory consumption.
All generated code runs as fast as your own, in pure DI style, including compile-time and run-time optimization. As mentioned above, graph analysis is done at compile time, and at runtime there are only a bunch of nested constructors, and that's it. Memory is spent only on the object graph being created.
- It works everywhere.
Since the pure DI approach does not use any dependencies or .NET reflection at runtime, it does not prevent the code from running as expected on any platform: Full .NET Framework 2.0+, .NET Core, .NET, UWP/XBOX, .NET IoT, Xamarin, Native AOT, etc.
- Ease of Use.
The Pure.DI API is very similar to the API of most IoC/DI libraries. And this was a conscious decision: the main reason is that programmers don't need to learn a new API.
- Superfine customization of generic types.
In Pure.DI it is proposed to use special marker types instead of using open generic types. This allows you to build the object graph more accurately and take full advantage of generic types.
- Supports the major .NET BCL types out of the box.
Pure.DI already supports many of BCL types like
Array
,IEnumerable<T>
,IList<T>
,IReadOnlyCollection<T>
,IReadOnlyList<T>
,ISet<T>
,IProducerConsumerCollection<T>
,ConcurrentBag<T>
,Func<T>
,ThreadLocal
,ValueTask<T>
,Task<T>
,MemoryPool<T>
,ArrayPool<T>
,ReadOnlyMemory<T>
,Memory<T>
,ReadOnlySpan<T>
,Span<T>
,IComparer<T>
,IEqualityComparer<T>
and etc. without any extra effort. - Good for building libraries or frameworks where resource consumption is particularly critical.
Its high performance, zero memory consumption/preparation overhead, and lack of dependencies make it ideal for building libraries and frameworks.
Schrödinger's cat will demonstrate how it all works
The reality is
Let's create an abstraction
interface IBox<out T>
{
T Content { get; }
}
interface ICat
{
State State { get; }
}
enum State { Alive, Dead }
Here's our implementation
record CardboardBox<T>(T Content): IBox<T>;
class ShroedingersCat(Lazy<State> superposition): ICat
{
// The decoherence of the superposition
// at the time of observation via an irreversible process
public State State => superposition.Value;
}
[!IMPORTANT] Our abstraction and implementation knows nothing about the magic of DI or any frameworks.
Let's glue it all together
Add the Pure.DI package to your project:
Let's bind the abstractions to their implementations and set up the creation of the object graph:
DI.Setup(nameof(Composition))
// Models a random subatomic event that may or may not occur
.Bind().As(Singleton).To<Random>()
// Quantum superposition of two states: Alive or Dead
.Bind().To((Random random) => (State)random.Next(2))
.Bind().To<ShroedingersCat>()
// Cardboard box with any contents
.Bind().To<CardboardBox<TT>>()
// Composition Root
.Root<Program>("Root");
[!NOTE] In fact, the
Bind().As(Singleton).To<Random>()
binding is unnecessary since Pure.DI supports many .NET BCL types out of the box, including Random. It was added just for the example of using the Singleton lifetime.
The above code specifies the generation of a partial class named Composition, this name is defined in the DI.Setup(nameof(Composition))
call. This class contains a Root property that returns a graph of objects with an object of type Program as the root. The type and name of the property is defined by calling Root<Program>("Root")
. The code of the generated class looks as follows:
partial class Composition
{
private Lock _lock = new Lock();
private Random? _random;
public Program Root
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
var stateFunc = new Func<State>(() => {
if (_random is null)
using (_lock.EnterScope())
if (_random is null)
_random = new Random();
return (State)_random.Next(2)
});
return new Program(
new CardboardBox<ICat>(
new ShroedingersCat(
new Lazy<State>(
stateFunc))));
}
}
public T Resolve<T>() { ... }
public object Resolve(Type type) { ... }
}
Obviously, this code does not depend on other libraries, does not use type reflection or any other tricks that can negatively affect performance and memory consumption. It looks like an efficient code written by hand. At any given time, you can study it and understand how it works.
The public Program Root { get; }
property here is a Composition Root, the only place in the application where the composition of the object graph for the application takes place. Each instance is created by only basic language constructs, which compiles with all optimizations with minimal impact on performance and memory consumption. In general, applications may have multiple composition roots and thus such properties. Each composition root must have its own unique name, which is defined when the Root<T>(string name)
method is called, as shown in the above code.
Time to open boxes!
class Program(IBox<ICat> box)
{
// Composition Root, a single place in an application
// where the composition of the object graphs
// for an application take place
static void Main() => new Composition().Root.Run();
private void Run() => Console.WriteLine(box);
}
Pure.DI creates efficient code in a pure DI paradigm, using only basic language constructs as if you were writing code by hand. This allows you to take full advantage of Dependency Injection everywhere and always, without any compromise!
The full analog of this application with top-level statements can be found here.
Just try creating a project from scratch!
Install the projects template
dotnet new install Pure.DI.Templates
In some directory, create a console application
dotnet new di
And run it
dotnet run
Examples
Basics
- Auto-bindings
- Injections of abstractions
- Composition roots
- Resolve methods
- Simplified binding
- Factory
- Simplified factory
- Class arguments
- Root arguments
- Tags
- Smart tags
- Build up of an existing object
- Field injection
- Method injection
- Property injection
- Default values
- Required properties or fields
- Root binding
- Async Root
- Consumer types
Lifetimes
- Transient
- Singleton
- PerResolve
- PerBlock
- Scope
- Auto scoped
- Default lifetime
- Default lifetime for a type
- Default lifetime for a type and a tag
- Disposable singleton
- Async disposable singleton
- Async disposable scope
Base Class Library
- Func
- Enumerable
- Enumerable generics
- Array
- Lazy
- Task
- ValueTask
- Manually started tasks
- Span and ReadOnlySpan
- Tuple
- Weak Reference
- Async Enumerable
- Service collection
- Func with arguments
- Func with tag
- Keyed service provider
- Service provider
- Service provider with scope
- Overriding the BCL binding
Generics
- Generics
- Generic composition roots
- Complex generics
- Generic composition roots with constraints
- Generic async composition roots with constraints
- Custom generic argument
- Build up of an existing generic object
- Generic root arguments
Attributes
- Constructor ordinal attribute
- Member ordinal attribute
- Tag attribute
- Type attribute
- Inject attribute
- Custom attributes
- Custom universal attribute
- Custom generic argument attribute
- Bind attribute
- Bind attribute with lifetime and tag
- Bind attribute for a generic type
Interception
Hints
- Resolve hint
- ThreadSafe hint
- OnDependencyInjection hint
- OnCannotResolve hint
- OnNewInstance hint
- ToString hint
- Check for a root
Advanced
- Composition root kinds
- Tag Type
- Tag Unique
- Tag on injection site
- Tag on a constructor argument
- Tag on a member
- Tag on a method argument
- Tag on injection site with wildcards
- Dependent compositions
- Accumulators
- Global compositions
- Partial class
- A few partial classes
- Tracking disposable instances per a composition root
- Tracking disposable instances in delegates
- Tracking disposable instances using pre-built classes
- Tracking disposable instances with different lifetimes
- Tracking async disposable instances per a composition root
- Tracking async disposable instances in delegates
- Exposed roots
- Exposed roots with tags
- Exposed roots via arg
- Exposed roots via root arg
- Exposed generic roots
- Exposed generic roots with args
- DI tracing via serilog
Applications
- Console
- UI
- Web
- Git repo with examples
Generated Code
Each generated class, hereafter called a composition, must be customized. Setup starts with a call to the Setup(string compositionTypeName)
method:
DI.Setup("Composition")
.Bind<IDependency>().To<Dependency>()
.Bind<IService>().To<Service>()
.Root<IService>("Root");
The following class will be generated
partial class Composition
{
// Default constructor
public Composition() { }
// Scope constructor
internal Composition(Composition parentScope) { }
// Composition root
public IService Root
{
get
{
return new Service(new Dependency());
}
}
public T Resolve<T>() { ... }
public T Resolve<T>(object? tag) { ... }
public object Resolve(Type type) { ... }
public object Resolve(Type type, object? tag) { ... }
}
The compositionTypeName parameter can be omitted
- if the setup is performed inside a partial class, then the composition will be created for this partial class
- for the case of a class with composition kind
CompositionKind.Global
, see this example
Setup arguments
The first parameter is used to specify the name of the composition class. All sets with the same name will be combined to create one composition class. Alternatively, this name may contain a namespace, e.g. a composition class is generated for Sample.Composition
:
namespace Sample
{
partial class Composition
{
...
}
}
The second optional parameter may have multiple values to determine the kind of composition.
CompositionKind.Public
This value is used by default. If this value is specified, a normal composition class will be created.
CompositionKind.Internal
If you specify this value, the class will not be generated, but this setup can be used by others as a base setup. For example:
DI.Setup("BaseComposition", CompositionKind.Internal)
.Bind().To<Dependency>();
DI.Setup("Composition").DependsOn("BaseComposition")
.Bind().To<Service>();
If the CompositionKind.Public flag is set in the composition setup, it can also be the base for other compositions, as in the example above.
CompositionKind.Global
No composition class will be created when this value is specified, but this setup is the base setup for all setups in the current project, and DependsOn(...)
is not required.
Constructors
Default constructor
It's quite trivial, this constructor simply initializes the internal state.
Parameterized constructor
It replaces the default constructor and is only created if at least one argument is specified. For example:
DI.Setup("Composition")
.Arg<string>("name")
.Arg<int>("id")
...
In this case, the constructor with arguments is as follows:
public Composition(string name, int id) { ... }
and there is no default constructor. It is important to remember that only those arguments that are used in the object graph will appear in the constructor. Arguments that are not involved cannot be defined, as they are omitted from the constructor parameters to save resources.
Scope constructor
This constructor creates a composition instance for the new scope. This allows Lifetime.Scoped
to be applied. See this example for details.
Composition Roots
Public Composition Roots
To create an object graph quickly and conveniently, a set of properties (or a methods) is formed. These properties/methods are here called roots of compositions. The type of a property/method is the type of the root object created by the composition. Accordingly, each invocation of a property/method leads to the creation of a composition with a root element of this type.
DI.Setup("Composition")
.Bind<IService>().To<Service>()
.Root<IService>("MyService");
In this case, the property for the IService type will be named MyService and will be available for direct use. The result of its use will be the creation of a composition of objects with the root of IService type:
public IService MyService
{
get
{
...
return new Service(...);
}
}
This is recommended way to create a composition root. A composition class can contain any number of roots.
Private Composition Roots
If the root name is empty, a private composition root with a random name is created:
private IService RootM07D16di_0001
{
get { ... }
}
This root is available in Resolve methods in the same way as public roots. For example:
DI.Setup("Composition")
.Bind<IService>().To<Service>()
.Root<IService>();
These properties have an arbitrary name and access modifier private and cannot be used directly from the code. Do not attempt to use them, as their names are arbitrarily changed. Private composition roots can be resolved by Resolve methods.
Methods "Resolve"
Methods "Resolve"
By default, a set of four Resolve methods is generated:
public T Resolve<T>() { ... }
public T Resolve<T>(object? tag) { ... }
public object Resolve(Type type) { ... }
public object Resolve(Type type, object? tag) { ... }
These methods can resolve both public and private composition roots that do not depend on any arguments of the composition roots. They are useful when using the Service Locator approach, where the code resolves composition roots in place:
var composition = new Composition();
composition.Resolve<IService>();
This is a not recommended way to create composition roots because Resolve methods have a number of disadvantages:
- They provide access to an unlimited set of dependencies.
- Their use can potentially lead to runtime exceptions, for example, when the corresponding root has not been defined.
- Lead to performance degradation because they search for the root of a composition based on its type.
To control the generation of these methods, see the Resolve hint.
Dispose and DisposeAsync
Provides a mechanism to release unmanaged resources. These methods are generated only if the composition contains at least one singleton/scoped instance that implements either the IDisposable and/or DisposeAsync interface. The Dispose()
or DisposeAsync()
method of the composition should be called to dispose of all created singleton/scoped objects:
using var composition = new Composition();
or
await using var composition = new Composition();
To dispose objects of other lifetimes please see this or this examples.
Setup hints
Setup hints
Hints are used to fine-tune code generation. Setup hints can be used as shown in the following example:
DI.Setup("Composition")
.Hint(Hint.Resolve, "Off")
.Hint(Hint.ThreadSafe, "Off")
.Hint(Hint.ToString, "On")
...
In addition, setup hints can be commented out before the Setup method as hint = value
. For example:
// Resolve = Off
// ThreadSafe = Off
DI.Setup("Composition")
...
Both approaches can be mixed:
// Resolve = Off
DI.Setup("Composition")
.Hint(Hint.ThreadSafe, "Off")
...
The list of hints will be gradually expanded to meet the needs and desires for fine-tuning code generation. Please feel free to add your ideas.
Resolve Hint
Determines whether to generate Resolve methods. By default, a set of four Resolve methods are generated. Set this hint to Off to disable the generation of resolve methods. This will reduce the generation time of the class composition, and in this case no private composition roots will be generated. The class composition will be smaller and will only have public roots. When the Resolve hint is disabled, only the public roots properties are available, so be sure to explicitly define them using the Root<T>(string name)
method with an explicit composition root name.
OnNewInstance Hint
Determines whether to use the OnNewInstance partial method. By default, this partial method is not generated. This can be useful, for example, for logging purposes:
internal partial class Composition
{
partial void OnNewInstance<T>(ref T value, object? tag, object lifetime) =>
Console.WriteLine($"'{typeof(T)}'('{tag}') created.");
}
You can also replace the created instance with a T
type, where T
is the actual type of the created instance. To minimize performance loss when calling OnNewInstance, use the three hints below.
OnNewInstancePartial Hint
Determines whether to generate the OnNewInstance partial method. By default, this partial method is generated when the OnNewInstance hint is On
.
OnNewInstanceImplementationTypeNameRegularExpression Hint
This is a regular expression for filtering by instance type name. This hint is useful when OnNewInstance is in On state and it is necessary to limit the set of types for which the OnNewInstance method will be called.
OnNewInstanceTagRegularExpression Hint
This is a regular expression for filtering by tag. This hint is also useful when OnNewInstance is in On state and it is necessary to limit the set of tags for which the OnNewInstance method will be called.
OnNewInstanceLifetimeRegularExpression Hint
This is a regular expression for filtering by lifetime. This hint is also useful when OnNewInstance is in On state and it is necessary to restrict the set of life times for which the OnNewInstance method will be called.
OnDependencyInjection Hint
Determines whether to use the OnDependencyInjection partial method when the OnDependencyInjection hint is On
to control dependency injection. By default it is On
.
// OnDependencyInjection = On
// OnDependencyInjectionPartial = Off
// OnDependencyInjectionContractTypeNameRegularExpression = ICalculator[\d]{1}
// OnDependencyInjectionTagRegularExpression = Abc
DI.Setup("Composition")
...
OnDependencyInjectionPartial Hint
Determines whether to generate the OnDependencyInjection partial method to control dependency injection. By default, this partial method is not generated. It cannot have an empty body because of the return value. It must be overridden when it is generated. This may be useful, for example, for Interception Scenario.
// OnDependencyInjection = On
// OnDependencyInjectionContractTypeNameRegularExpression = ICalculator[\d]{1}
// OnDependencyInjectionTagRegularExpression = Abc
DI.Setup("Composition")
...
To minimize performance loss when calling OnDependencyInjection, use the three tips below.
OnDependencyInjectionImplementationTypeNameRegularExpression Hint
This is a regular expression for filtering by instance type name. This hint is useful when OnDependencyInjection is in On state and it is necessary to restrict the set of types for which the OnDependencyInjection method will be called.
OnDependencyInjectionContractTypeNameRegularExpression Hint
This is a regular expression for filtering by the name of the resolving type. This hint is also useful when OnDependencyInjection is in On state and it is necessary to limit the set of permissive types for which the OnDependencyInjection method will be called.
OnDependencyInjectionTagRegularExpression Hint
This is a regular expression for filtering by tag. This hint is also useful when OnDependencyInjection is in the On state and you want to limit the set of tags for which the OnDependencyInjection method will be called.
OnDependencyInjectionLifetimeRegularExpression Hint
This is a regular expression for filtering by lifetime. This hint is also useful when OnDependencyInjection is in On state and it is necessary to restrict the set of lifetime for which the OnDependencyInjection method will be called.
OnCannotResolve Hint
Determines whether to use the OnCannotResolve<T>(...)
partial method to handle a scenario in which an instance cannot be resolved. By default, this partial method is not generated. Because of the return value, it cannot have an empty body and must be overridden at creation.
// OnCannotResolve = On
// OnCannotResolveContractTypeNameRegularExpression = string|DateTime
// OnDependencyInjectionTagRegularExpression = null
DI.Setup("Composition")
...
To avoid missing failed bindings by mistake, use the two relevant hints below.
OnCannotResolvePartial Hint
Determines whether to generate the OnCannotResolve<T>(...)
partial method when the OnCannotResolve hint is On
.
// OnCannotResolve = On
// OnCannotResolvePartial = Off
// OnCannotResolveContractTypeNameRegularExpression = string|DateTime
// OnDependencyInjectionTagRegularExpression = null
DI.Setup("Composition")
...
To avoid missing failed bindings by mistake, use the two relevant hints below.
OnNewRoot Hint
Determines whether to use a static partial method OnNewRoot<TContract, T>(...)
to handle the new composition root registration event.
// OnNewRoot = On
DI.Setup("Composition")
...
Be careful, this hint disables checks for the ability to resolve dependencies!
OnNewRootPartial Hint
Determines whether to generate a static partial method OnNewRoot<TContract, T>(...)
when the OnNewRoot hint is On
to handle the new composition root registration event.
// OnNewRootPartial = Off
DI.Setup("Composition")
...
OnCannotResolveContractTypeNameRegularExpression Hint
This is a regular expression for filtering by the name of the resolving type. This hint is also useful when OnCannotResolve is in On state and it is necessary to limit the set of resolving types for which the OnCannotResolve method will be called.
OnCannotResolveTagRegularExpression Hint
This is a regular expression for filtering by tag. This hint is also useful when OnCannotResolve is in On state and it is necessary to limit the set of tags for which the OnCannotResolve method will be called.
OnCannotResolveLifetimeRegularExpression Hint
This is a regular expression for filtering by lifetime. This hint is also useful when OnCannotResolve is in the On state and it is necessary to restrict the set of lives for which the OnCannotResolve method will be called.
ToString Hint
Determines whether to generate the ToString() method. This method provides a class diagram in mermaid format. To see this diagram, just call the ToString method and copy the text to this site.
// ToString = On
DI.Setup("Composition")
.Bind<IService>().To<Service>()
.Root<IService>("MyService");
var composition = new Composition();
string classDiagram = composition.ToString();
ThreadSafe Hint
This hint determines whether the composition of objects will be created in a thread-safe way. The default value of this hint is On. It is a good practice not to use threads when creating an object graph, in this case the hint can be disabled, which will result in a small performance gain. For example:
// ThreadSafe = Off
DI.Setup("Composition")
.Bind<IService>().To<Service>()
.Root<IService>("MyService");
ResolveMethodModifiers Hint
Overrides the modifiers of the public T Resolve<T>()
method.
ResolveMethodName Hint
Overrides the method name for public T Resolve<T>()
.
ResolveByTagMethodModifiers Hint
Overrides the modifiers of the public T Resolve<T>(object? tag)
method.
ResolveByTagMethodName Hint
Overrides the method name for public T Resolve<T>(object? tag)
.
ObjectResolveMethodModifiers Hint
Overrides the modifiers of the public object Resolve(Type type)
method.
ObjectResolveMethodName Hint
Overrides the method name for public object Resolve(Type type)
.
ObjectResolveByTagMethodModifiers Hint
Overrides the modifiers of the public object Resolve(Type type, object? tag)
method.
ObjectResolveByTagMethodName Hint
Overrides the method name for public object Resolve(Type type, object? tag)
.
DisposeMethodModifiers Hint
Overrides the modifiers of the public void Dispose()
method.
DisposeAsyncMethodModifiers Hint
Overrides the modifiers of the public ValueTask DisposeAsync()
method.
FormatCode Hint
Specifies whether the generated code should be formatted. This option consumes a lot of CPU resources. This hint may be useful when studying the generated code or, for example, when making presentations.
SeverityOfNotImplementedContract Hint
Indicates the severity level of the situation when, in the binding, an implementation does not implement a contract. Possible values:
- "Error", it is default value.
- "Warning" - something suspicious but allowed.
- "Info" - information that does not indicate a problem.
- "Hidden" - what's not a problem.
Comments Hint
Specifies whether the generated code should be commented.
// Represents the composition class
DI.Setup(nameof(Composition))
.Bind<IService>().To<Service>()
// Provides a composition root of my service
.Root<IService>("MyService");
Appropriate comments will be added to the generated Composition
class and the documentation for the class, depending on the IDE used, will look something like this:
Then documentation for the composition root:
NuGet packages
Pure.DI | DI Source code generator | |
Pure.DI.Abstractions | Abstractions for Pure.DI | |
Pure.DI.Templates | Template Package you can call from the shell/command line. | |
Pure.DI.MS | Tools for working with Microsoft DI |
Project template
Install the DI template Pure.DI.Templates
dotnet new install Pure.DI.Templates
Create a "Sample" console application from the template di
dotnet new di -o ./Sample
And run it
dotnet run --project Sample
For more information about the template, please see this page.
Troubleshooting
Version update
When updating the version, it is possible that the previous version of the code generator remains active and is used by compilation services. In this case, the old and new versions of the generator may conflict. For a project where the code generator is used, it is recommended to do the following:
- After updating the version, close the IDE if it is open
- Delete the obj and bin directories
- Execute the following commands one by one
dotnet build-server shutdown
dotnet restore
dotnet build
Disabling API generation
Pure.DI automatically generates its API. If an assembly already has the Pure.DI API, for example, from another assembly, it is sometimes necessary to disable its automatic generation to avoid ambiguity. To do this, you need to add a DefineConstants element to the project files of these modules. For example:
<PropertyGroup>
<DefineConstants>$(DefineConstants);PUREDI_API_SUPPRESSION</DefineConstants>
</PropertyGroup>
Display generated files
You can set project properties to save generated files and control their storage location. In the project file, add the <EmitCompilerGeneratedFiles>
element to the <PropertyGroup>
group and set its value to true
. Build the project again. The generated files are now created in the obj/Debug/netX.X/generated/Pure.DI/Pure.DI/Pure.DI.SourceGenerator directory. The path components correspond to the build configuration, the target framework, the source generator project name, and the full name of the generator type. You can choose a more convenient output folder by adding the <CompilerGeneratedFilesOutputPath>
element to the application project file. For example:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)Generated</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
</Project>
Contribution
Thank you for your interest in contributing to the Pure.DI project! First of all, if you are going to make a big change or feature, please open a problem first. That way, we can coordinate and understand if the change you're going to work on fits with current priorities and if we can commit to reviewing and merging it within a reasonable timeframe. We don't want you to waste a lot of your valuable time on something that may not align with what we want for Pure.DI.
Contribution prerequisites: .NET SDK 9.0 or later is installed.
The entire build logic is a regular console .NET application. You can use the build.cmd and build.sh files with the appropriate command in the parameters to perform all basic actions on the project, e.g:
Command | Description |
---|---|
g, generator | Builds and tests generator |
l, libs | Builds and tests libraries |
c, check | Compatibility checks |
p, pack | Creates NuGet packages |
r, readme | Generates README.md |
benchmarks, bm | Runs benchmarks |
deploy, dp | Deploys packages |
t, template | Creates and deploys templates |
u, upgrade | Upgrading the internal version of DI to the latest public version |
For example:
./build.sh pack
./build.cmd benchmarks
If you are using the Rider IDE, it already has a set of configurations to run these commands. This project uses C# interactive build automation system for .NET. This tool helps to make .NET builds more efficient.
Additional resources
Examples of how to set up a composition
Articles
RU DotNext video

Benchmarks environment
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.4894/22H2/2022Update) AMD Ryzen 9 5900X, 1 CPU, 24 logical and 12 physical cores .NET SDK 9.0.100 [Host] : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2 DefaultJob : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
About
Constructing injecting container
How to use
Example ( source csproj, source files )
- CSharp Project
- Program.cs
- DatabaseCon.cs
- Database.cs
- IDatabase.cs
This is the CSharp Project that references Pure.DI
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Pure.DI" Version="2.1.44">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
This is the use of Pure.DI in Program.cs
using InjectDemo;
using Pure.DI;
DI.Setup("Composition")
.Bind<IDatabase>().To<DatabaseCon>()
.Bind<Database>().To<Database>()
.Root<Database>();
;
var c = new Composition();
var con = c.Resolve<Database>();
con.Open();
This is the use of Pure.DI in DatabaseCon.cs
namespace InjectDemo;
public partial class DatabaseCon:IDatabase
{
public string? Connection { get; set; }
public void Open()
{
Console.WriteLine("open from database con" );
}
}
This is the use of Pure.DI in Database.cs
namespace InjectDemo;
partial class Database : IDatabase
{
private readonly IDatabase con;
public Database(IDatabase con)
{
this.con = con;
}
public void Open()
{
Console.WriteLine($"open from database");
con.Open();
}
}
This is the use of Pure.DI in IDatabase.cs
namespace InjectDemo
{
internal interface IDatabase
{
public void Open();
}
}
Generated Files
Those are taken from $(BaseIntermediateOutputPath)\GX
- Composition.g.cs
- Pure.DI.Components.Api.g.cs
- Pure.DI.Components.GenericTypeArguments.g.cs
- Pure.DI.Features.Default.g.cs
// <auto-generated/>
// by Pure.DI 2.1.44+4da6876f3ecd7c34771553d8409b829e287d3041
#nullable enable annotations
using Pure.DI;
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// <para>
/// <b>Composition roots:</b><br/>
/// <list type="bullet">
/// <item>
/// <term>
/// Private composition root of type <see cref="InjectDemo.Database"/>. It can be resolved by <see cref="Resolve{T}()"/> method: <c>Resolve<global::InjectDemo.Database>()</c>
/// </term>
/// <description>
/// Provides a composition root of type <see cref="InjectDemo.Database"/>.
/// </description>
/// </item>
/// </list>
/// </para>
/// <br/>
/// <br/>This class was created by <a href="https://github.com/DevTeam/Pure.DI">Pure.DI</a> source code generator.
/// </summary>
#if !NET20 && !NET35 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6 && !NETCOREAPP1_0 && !NETCOREAPP1_1
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
partial class Composition
{
private readonly Composition _rootM01D21di;
/// <summary>
/// This constructor creates a new instance of <see cref="Composition"/>.
/// </summary>
[global::Pure.DI.OrdinalAttribute(256)]
public Composition()
{
_rootM01D21di = this;
}
/// <summary>
/// This constructor creates a new instance of <see cref="Composition"/> scope based on <paramref name="parentScope"/>. This allows the <see cref="Lifetime.Scoped"/> life time to be applied.
/// </summary>
/// <param name="parentScope">Scope parent.</param>
internal Composition(Composition parentScope)
{
_rootM01D21di = (parentScope ?? throw new global::System.ArgumentNullException(nameof(parentScope)))._rootM01D21di;
}
#region Roots
/// <summary>
/// <para>
/// Provides a composition root of type <see cref="InjectDemo.Database"/>.
/// </para>
/// </summary>
private global::InjectDemo.Database RootM01D21di1
{
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
get
{
return new global::InjectDemo.Database(new global::InjectDemo.DatabaseCon());
}
}
#endregion
#region API
/// <summary>
/// Resolves the composition root.
/// </summary>
/// <typeparam name="T">The type of the composition root.</typeparam>
/// <returns>A composition root.</returns>
#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP || NET40_OR_GREATER || NET
[global::System.Diagnostics.Contracts.Pure]
#endif
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public T Resolve<T>()
{
return ResolverM01D21di<T>.Value.Resolve(this);
}
/// <summary>
/// Resolves the composition root by tag.
/// </summary>
/// <typeparam name="T">The type of the composition root.</typeparam>
/// <param name="tag">The tag of a composition root.</param>
/// <returns>A composition root.</returns>
#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP || NET40_OR_GREATER || NET
[global::System.Diagnostics.Contracts.Pure]
#endif
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public T Resolve<T>(object? tag)
{
return ResolverM01D21di<T>.Value.ResolveByTag(this, tag);
}
/// <summary>
/// Resolves the composition root.
/// </summary>
/// <param name="type">The type of the composition root.</param>
/// <returns>A composition root.</returns>
#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP || NET40_OR_GREATER || NET
[global::System.Diagnostics.Contracts.Pure]
#endif
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public object Resolve(global::System.Type type)
{
var index = (int)(_bucketSizeM01D21di * ((uint)global::System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(type) % 1));
ref var pair = ref _bucketsM01D21di[index];
return pair.Key == type ? pair.Value.Resolve(this) : ResolveM01D21di(type, index);
}
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private object ResolveM01D21di(global::System.Type type, int index)
{
var finish = index + _bucketSizeM01D21di;
while (++index < finish)
{
ref var pair = ref _bucketsM01D21di[index];
if (pair.Key == type)
{
return pair.Value.Resolve(this);
}
}
throw new global::System.InvalidOperationException($"{CannotResolveMessageM01D21di} {OfTypeMessageM01D21di} {type}.");
}
/// <summary>
/// Resolves the composition root by tag.
/// </summary>
/// <param name="type">The type of the composition root.</param>
/// <param name="tag">The tag of a composition root.</param>
/// <returns>A composition root.</returns>
#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP || NET40_OR_GREATER || NET
[global::System.Diagnostics.Contracts.Pure]
#endif
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public object Resolve(global::System.Type type, object? tag)
{
var index = (int)(_bucketSizeM01D21di * ((uint)global::System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(type) % 1));
ref var pair = ref _bucketsM01D21di[index];
return pair.Key == type ? pair.Value.ResolveByTag(this, tag) : ResolveM01D21di(type, tag, index);
}
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private object ResolveM01D21di(global::System.Type type, object? tag, int index)
{
var finish = index + _bucketSizeM01D21di;
while (++index < finish)
{
ref var pair = ref _bucketsM01D21di[index];
if (pair.Key == type)
{
return pair.Value.ResolveByTag(this, tag);
}
}
throw new global::System.InvalidOperationException($"{CannotResolveMessageM01D21di} \"{tag}\" {OfTypeMessageM01D21di} {type}.");
}
#endregion
private readonly static int _bucketSizeM01D21di;
private readonly static global::Pure.DI.Pair<global::System.Type, global::Pure.DI.IResolver<Composition, object>>[] _bucketsM01D21di;
static Composition()
{
var valResolverM01D21di_0000 = new ResolverM01D21di_0000();
ResolverM01D21di<global::InjectDemo.Database>.Value = valResolverM01D21di_0000;
_bucketsM01D21di = global::Pure.DI.Buckets<global::System.Type, global::Pure.DI.IResolver<Composition, object>>.Create(
1,
out _bucketSizeM01D21di,
new global::Pure.DI.Pair<global::System.Type, global::Pure.DI.IResolver<Composition, object>>[1]
{
new global::Pure.DI.Pair<global::System.Type, global::Pure.DI.IResolver<Composition, object>>(typeof(InjectDemo.Database), valResolverM01D21di_0000)
});
}
#region Resolvers
private const string CannotResolveMessageM01D21di = "Cannot resolve composition root ";
private const string OfTypeMessageM01D21di = "of type ";
private class ResolverM01D21di<T>: global::Pure.DI.IResolver<Composition, T>
{
public static global::Pure.DI.IResolver<Composition, T> Value = new ResolverM01D21di<T>();
public virtual T Resolve(Composition composite)
{
throw new global::System.InvalidOperationException($"{CannotResolveMessageM01D21di}{OfTypeMessageM01D21di}{typeof(T)}.");
}
public virtual T ResolveByTag(Composition composite, object tag)
{
throw new global::System.InvalidOperationException($"{CannotResolveMessageM01D21di}\"{tag}\" {OfTypeMessageM01D21di}{typeof(T)}.");
}
}
private sealed class ResolverM01D21di_0000: ResolverM01D21di<InjectDemo.Database>
{
public override InjectDemo.Database Resolve(Composition composition)
{
return composition.RootM01D21di1;
}
public override InjectDemo.Database ResolveByTag(Composition composition, object tag)
{
switch (tag)
{
case null:
return composition.RootM01D21di1;
default:
return base.ResolveByTag(composition, tag);
}
}
}
#endregion
}
// <auto-generated/>
#if !PUREDI_API_SUPPRESSION || PUREDI_API_V2
#pragma warning disable
#if !PUREDI_API_FUNC_SUPPRESSION
namespace System
{
#if NET20
internal delegate TResult Func<out TResult>();
#endif
#if NET20 || NET35
internal delegate TResult Func<in T, out TResult>(T arg);
internal delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
internal delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3);
internal delegate TResult Func<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
#endif
}
#endif
namespace Pure.DI
{
using System;
/// <summary>
/// Binding lifetimes.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind<IDependency>().As(Lifetime.Singleton).To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="Pure.DI.DI.Setup"/>
/// <seealso cref="IBinding.As"/>
/// <seealso cref="IConfiguration.DefaultLifetime"/>
internal enum Lifetime
{
/// <summary>
/// Specifies to create a new dependency instance each time. This is the default value and can be omitted.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind<IDependency>().As(Lifetime.Transient).To<Dependency>();
/// </code>
/// This is the default lifetime, it can be omitted, for example:
/// <code>
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
Transient,
/// <summary>
/// Ensures that there will be a single instance of the dependency for each composition.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind<IDependency>().As(Lifetime.Singleton).To<Dependency>();
/// </code>
/// </example>
/// </summary>
Singleton,
/// <summary>
/// Guarantees that there will be a single instance of the dependency for each root of the composition.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind<IDependency>().As(Lifetime.PerResolve).To<Dependency>();
/// </code>
/// </example>
/// </summary>
PerResolve,
/// <summary>
/// Does not guarantee that there will be a single instance of the dependency for each root of the composition, but is useful to reduce the number of instances of type.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind<IDependency>().As(Lifetime.PerBlock).To<Dependency>();
/// </code>
/// </example>
/// </summary>
PerBlock,
/// <summary>
/// Ensures that there will be a single instance of the dependency for each scope.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind<IDependency>().As(Lifetime.Singleton).To<Dependency>();
/// </code>
/// </example>
/// </summary>
Scoped
}
/// <summary>
/// Hints for the code generator and can be used to fine tune code generation.
/// <example>
/// <code>
/// // Resolve = Off
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.Resolve, "Off")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
internal enum Hint
{
/// <summary>
/// <c>On</c> or <c>Off</c>. Determines whether to generate <c>Resolve</c> methods. <c>On</c> by default.
/// <example>
/// <code>
/// // Resolve = Off
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.Resolve, "Off")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
Resolve,
/// <summary>
/// <c>On</c> or <c>Off</c>. Determines whether to use partial <c>OnNewInstance</c> method. <c>Off</c> by default.
/// <example>
/// <code>
/// // OnNewInstance = On
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnNewInstance, "On")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnNewInstance,
/// <summary>
/// <c>On</c> or <c>Off</c>. Determines whether to generate partial <c>OnNewInstance</c> method when the _OnNewInstance_ hint is <c>On</c>. <c>On</c> by default.
/// <example>
/// <code>
/// // OnNewInstancePartial = On
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnNewInstancePartial, "On")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnNewInstancePartial,
/// <summary>
/// The regular expression to filter OnNewInstance by the instance type name. ".+" by default.
/// <example>
/// <code>
/// // OnNewInstanceImplementationTypeNameRegularExpression = Dependency
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnNewInstanceImplementationTypeNameRegularExpression, "Dependency")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnNewInstanceImplementationTypeNameRegularExpression,
/// <summary>
/// The regular expression to filter OnNewInstance by the tag. ".+" by default.
/// <example>
/// <code>
/// // OnNewInstanceTagRegularExpression = IDependency
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnNewInstanceTagRegularExpression, "IDependency")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnNewInstanceTagRegularExpression,
/// <summary>
/// The regular expression to filter OnNewInstance by the lifetime. ".+" by default.
/// <example>
/// <code>
/// // OnNewInstanceLifetimeRegularExpression = Singleton
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnNewInstanceLifetimeRegularExpression, "Singleton")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnNewInstanceLifetimeRegularExpression,
/// <summary>
/// <c>On</c> or <c>Off</c>. Determines whether to use partial <c>OnDependencyInjection</c> method to control of dependency injection. <c>Off</c> by default.
/// <example>
/// <code>
/// // OnDependencyInjection = On
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnDependencyInjection, "On")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnDependencyInjection,
/// <summary>
/// <c>On</c> or <c>Off</c>. Determines whether to generate partial <c>OnDependencyInjection</c> method when the _OnDependencyInjection_ hint is <c>On</c> to control of dependency injection. <c>On</c> by default.
/// <example>
/// <code>
/// // OnDependencyInjectionPartial = On
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnDependencyInjectionPartial, "On")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnDependencyInjectionPartial,
/// <summary>
/// The regular expression to filter OnDependencyInjection by the instance type name. ".+" by default.
/// <example>
/// <code>
/// // OnDependencyInjectionImplementationTypeNameRegularExpression = Dependency
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnDependencyInjectionImplementationTypeNameRegularExpression, "Dependency")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnDependencyInjectionImplementationTypeNameRegularExpression,
/// <summary>
/// The regular expression to filter OnDependencyInjection by the resolving type name. ".+" by default.
/// <example>
/// <code>
/// // OnDependencyInjectionContractTypeNameRegularExpression = IDependency
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnDependencyInjectionContractTypeNameRegularExpression, "IDependency")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnDependencyInjectionContractTypeNameRegularExpression,
/// <summary>
/// The regular expression to filter OnDependencyInjection by the tag. ".+" by default.
/// <example>
/// <code>
/// // OnDependencyInjectionTagRegularExpression = MyTag
/// DI.Setup("Composition")
/// .Bind<IDependency>("MyTag").To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnDependencyInjectionTagRegularExpression, "MyTag")
/// .Bind<IDependency>("MyTag").To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnDependencyInjectionTagRegularExpression,
/// <summary>
/// The regular expression to filter OnDependencyInjection by the lifetime. ".+" by default.
/// <example>
/// <code>
/// // OnDependencyInjectionLifetimeRegularExpression = Singleton
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnDependencyInjectionLifetimeRegularExpression, "Singleton")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnDependencyInjectionLifetimeRegularExpression,
/// <summary>
/// <c>On</c> or <c>Off</c>. Determines whether to use a partial <c>OnCannotResolve<T>(...)</c> method to handle a scenario in which the dependency cannot be resolved. <c>Off</c> by default.
/// <example>
/// <code>
/// // OnCannotResolve = On
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnCannotResolve, "On")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnCannotResolve,
/// <summary>
/// <c>On</c> or <c>Off</c>. Determines whether to generate a partial <c>OnCannotResolve<T>(...)</c> method when the <c>OnCannotResolve</c> hint is <c>On</c> to handle a scenario in which the dependency cannot be resolved. <c>On</c> by default.
/// <example>
/// <code>
/// // OnCannotResolvePartial = On
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnCannotResolvePartial, "On")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnCannotResolvePartial,
/// <summary>
/// The regular expression to filter OnCannotResolve by the resolving type name. ".+" by default.
/// <example>
/// <code>
/// // OnCannotResolveContractTypeNameRegularExpression = OtherType
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnCannotResolveContractTypeNameRegularExpression, "OtherType")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnCannotResolveContractTypeNameRegularExpression,
/// <summary>
/// The regular expression to filter OnCannotResolve by the tag. ".+" by default.
/// <example>
/// <code>
/// // OnCannotResolveTagRegularExpression = MyTag
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnCannotResolveTagRegularExpression, "MyTag")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnCannotResolveTagRegularExpression,
/// <summary>
/// The regular expression to filter OnCannotResolve by the lifetime. ".+" by default.
/// <example>
/// <code>
/// // OnCannotResolveLifetimeRegularExpression = Singleton
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnCannotResolveLifetimeRegularExpression, "Singleton")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnCannotResolveLifetimeRegularExpression,
/// <summary>
/// <c>On</c> or <c>Off</c>. Determines whether to use a static partial <c>OnNewRoot<T>(...)</c> method to handle the new Composition root registration event. <c>Off</c> by default.
/// <example>
/// <code>
/// // OnNewRoot = On
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnNewRoot, "On")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnNewRoot,
/// <summary>
/// <c>On</c> or <c>Off</c>. Determines whether to generate a static partial <c>OnNewRoot<T>(...)</c> method when the <c>OnNewRoot</c> hint is <c>On</c> to handle the new Composition root registration event. <c>On</c> by default.
/// <example>
/// <code>
/// // OnNewRootPartial = On
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.OnNewRootPartial, "On")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
OnNewRootPartial,
/// <summary>
/// <c>On</c> or <c>Off</c>. Determine if the <c>ToString()</c> method should be generated. This method provides a text-based class diagram in the format mermaid. <c>Off</c> by default.
/// <example>
/// <code>
/// // ToString = On
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.ToString, "On")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
ToString,
/// <summary>
/// <c>On</c> or <c>Off</c>. This hint determines whether object Composition will be created in a thread-safe manner. <c>On</c> by default.
/// <example>
/// <code>
/// // ThreadSafe = Off
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.ThreadSafe, "Off")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
ThreadSafe,
/// <summary>
/// Overrides modifiers of the method <c>public T Resolve<T>()</c>. "public" by default.
/// <example>
/// <code>
/// // ResolveMethodModifiers = internal
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.ResolveMethodModifiers, "internal")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
ResolveMethodModifiers,
/// <summary>
/// Overrides name of the method <c>public T Resolve<T>()</c>. "Resolve" by default.
/// <example>
/// <code>
/// // ResolveMethodName = GetService
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.ResolveMethodName, "GetService")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
ResolveMethodName,
/// <summary>
/// Overrides modifiers of the method <c>public T Resolve<T>(object? tag)</c>. "public" by default.
/// <example>
/// <code>
/// // ResolveByTagMethodModifiers = internal
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.ResolveByTagMethodModifiers, "internal")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
ResolveByTagMethodModifiers,
/// <summary>
/// Overrides name of the method <c>public T Resolve<T>(object? tag)</c>. "Resolve" by default.
/// <example>
/// For example:
/// <code>
/// // ResolveByTagMethodName = GetService
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.ResolveByTagMethodName, "GetService")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
ResolveByTagMethodName,
/// <summary>
/// Overrides modifiers of the method <c>public object Resolve(Type type)</c>. "public" by default.
/// <example>
/// <code>
/// // ObjectResolveMethodModifiers = internal
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.ObjectResolveMethodModifiers, "internal")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
ObjectResolveMethodModifiers,
/// <summary>
/// Overrides name of the method <c>public object Resolve(Type type)</c>. "Resolve" by default.
/// <example>
/// <code>
/// // ObjectResolveMethodName = GetService
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.ObjectResolveMethodName, "GetService")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
ObjectResolveMethodName,
/// <summary>
/// Overrides modifiers of the method <c>public object Resolve(Type type, object? tag)</c>. "public" by default.
/// <example>
/// <code>
/// // ObjectResolveByTagMethodModifiers = internal
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.ObjectResolveByTagMethodModifiers, "internal")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
ObjectResolveByTagMethodModifiers,
/// <summary>
/// Overrides name of the method <c>public object Resolve(Type type, object? tag)</c>. "Resolve" by default.
/// <example>
/// <code>
/// // ObjectResolveByTagMethodName = GetService
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.ObjectResolveByTagMethodName, "GetService")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
ObjectResolveByTagMethodName,
/// <summary>
/// Overrides modifiers of the method <c>public void Dispose()</c>. "public" by default.
/// <example>
/// <code>
/// // DisposeMethodModifiers = internal
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.DisposeMethodModifiers, "internal")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
DisposeMethodModifiers,
/// <summary>
/// Overrides modifiers of the method <c>public <see cref="ValueTask"/> DisposeAsyncMethodModifiers()</c>. "public" by default.
/// <example>
/// <code>
/// // DisposeAsyncMethodModifiers = internal
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.DisposeAsyncMethodModifiers, "internal")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
DisposeAsyncMethodModifiers,
/// <summary>
/// <c>On</c> or <c>Off</c>. Specifies whether the generated code should be formatted. This option consumes a lot of CPU resources. <c>Off</c> by default.
/// <example>
/// <code>
/// // FormatCode = On
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.FormatCode, "On")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
FormatCode,
/// <summary>
/// <c>Error</c> or <c>Warning</c> or <c>Info</c> or <c>Hidden</c>. Indicates the severity level of the situation when, in the binding, an implementation does not implement a contract. <c>Error</c> by default.
/// <example>
/// <code>
/// // FormatCode = On
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.SeverityOfNotImplementedContracts, "On")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
SeverityOfNotImplementedContract,
/// <summary>
/// <c>On</c> or <c>Off</c>. Specifies whether the generated code should be commented. <c>On</c> by default.
/// <example>
/// <code>
/// // Comments = Off
/// DI.Setup("Composition")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// <br/>
/// or using the API call <see cref="IConfiguration.Hint"/>:
/// <code>
/// DI.Setup("Composition")
/// .Hint(Hint.Comments, "Off")
/// .Bind<IDependency>().To<Dependency>();
/// </code>
/// </example>
/// </summary>
/// <seealso cref="IConfiguration.Hint"/>
Comments
}
/// <summary>
/// Represents a generic type argument attribute. It allows you to create custom generic type argument such as <see cref="TTS"/>, <see cref="TTDictionary{TKey,TValue}"/>, etc.
/// <example>
/// <code>
/// [GenericTypeArgument]
/// internal interface TTMy: IMy { }
/// </code>
/// </example>
/// </summary>
[global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Interface | global::System.AttributeTargets.Struct | global::System.AttributeTargets.Enum)]
#if !NET20 && !NET35 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6 && !NETCOREAPP1_0 && !NETCOREAPP1_1
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
internal sealed class GenericTypeArgumentAttribute : global::System.Attribute { }
/// <summary>
/// Represents an ordinal attribute.
/// This attribute is part of the API, but you can use your own attribute at any time, and this allows you to define them in the assembly and namespace you want.
/// <example>
/// For constructors, it defines the sequence of attempts to use a particular constructor to create an object:
/// <code>
/// class Service : IService
/// {
/// private readonly string _name;
///
///
/// [Ordinal(1)]
/// public Service(IDependency dependency) =>
/// _name = "with dependency";
///
///
/// [Ordinal(0)]
/// public Service(string name) => _name = name;
/// }
/// </code>
/// <br/>
/// For fields, properties and methods, it specifies to perform dependency injection and defines the sequence:
/// <code>
/// class Person : IPerson
/// {
/// private readonly string _name = "";
///
/// [Ordinal(0)]
/// public int Id;
///
///
/// [Ordinal(1)]
/// public string FirstName
/// {
/// set
/// {
/// _name = value;
/// }
/// }
///
///
/// public IDependency? Dependency { get; private set; }
///
///
/// [Ordinal(2)]
/// public void SetDependency(IDependency dependency) =>
/// Dependency = dependency;
/// }
/// </code>
/// </example>
/// </summary>
/// <seealso cref="TagAttribute"/>
/// <seealso cref="TypeAttribute"/>
[global::System.AttributeUsage(global::System.AttributeTargets.Constructor | global::System.AttributeTargets.Method | global::System.AttributeTargets.Property | global::System.AttributeTargets.Field, AllowMultiple = false)]
#if !NET20 && !NET35 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6 && !NETCOREAPP1_0 && !NETCOREAPP1_1
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
internal class OrdinalAttribute : global::System.Attribute
{
/// <summary>
/// Creates an attribute instance.
/// </summary>
/// <param name="ordinal">The injection ordinal.</param>
public OrdinalAttribute(int ordinal) { }
}
/// <summary>
/// Represents a tag attribute overriding an injection tag. The tag can be a constant, a type, or a value of an enumerated type.
/// This attribute is part of the API, but you can use your own attribute at any time, and this allows you to define them in the assembly and namespace you want.
/// <example>
/// Sometimes it's important to take control of building a dependency graph. For example, when there are multiple implementations of the same contract. In this case, tags will help:
/// <code>
/// interface IDependency { }
///
///
/// class AbcDependency : IDependency { }
///
///
/// class XyzDependency : IDependency { }
///
///
/// class Dependency : IDependency { }
///
///
/// interface IService
/// {
/// IDependency Dependency1 { get; }
///
///
/// IDependency Dependency2 { get; }
/// }
///
///
/// class Service : IService
/// {
/// public Service(
/// [Tag("Abc")] IDependency dependency1,
/// [Tag("Xyz")] IDependency dependency2)
/// {
/// Dependency1 = dependency1;
/// Dependency2 = dependency2;
/// }
///
/// public IDependency Dependency1 { get; }
///
///
/// public IDependency Dependency2 { get; }
/// }
///
///
/// DI.Setup("Composition")
/// .Bind<IDependency>("Abc").To<AbcDependency>()
/// .Bind<IDependency>("Xyz").To<XyzDependency>()
/// .Bind<IService>().To<Service>().Root<IService>("Root");
/// </code>
/// </example>
/// </summary>
/// <seealso cref="OrdinalAttribute"/>
/// <seealso cref="TypeAttribute"/>
[global::System.AttributeUsage(global::System.AttributeTargets.Parameter | global::System.AttributeTargets.Property | global::System.AttributeTargets.Field, AllowMultiple = false)]
#if !NET20 && !NET35 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6 && !NETCOREAPP1_0 && !NETCOREAPP1_1
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
internal class TagAttribute : global::System.Attribute
{
/// <summary>
/// Creates an attribute instance.
/// </summary>
/// <param name="tag">The injection tag. See also <see cref="IBinding.Tags"/></param>.
public TagAttribute(object tag) { }
}
/// <summary>
/// The injection type can be defined manually using the <c>Type</c> attribute.This attribute explicitly overrides an injected type, otherwise it would be determined automatically based on the type of the constructor/method, property, or field parameter.
/// This attribute is part of the API, but you can use your own attribute at any time, and this allows you to define them in the assembly and namespace you want.
/// <example>
/// <code>
/// interface IDependency { }
///
///
/// class AbcDependency : IDependency { }
///
///
/// class XyzDependency : IDependency { }
///
///
/// interface IService
/// {
/// IDependency Dependency1 { get; }
///
/// IDependency Dependency2 { get; }
/// }
///
///
/// class Service : IService
/// {
/// public Service(
/// [Type(typeof(AbcDependency))] IDependency dependency1,
/// [Type(typeof(XyzDependency))] IDependency dependency2)
/// {
/// Dependency1 = dependency1;
/// Dependency2 = dependency2;
/// }
///
///
/// public IDependency Dependency1 { get; }
///
///
/// public IDependency Dependency2 { get; }
/// }
///
///
/// DI.Setup("Composition")
/// .Bind<IService>().To<Service>().Root<IService>("Root");
/// </code>
/// </example>
/// </summary>
/// <seealso cref="TagAttribute"/>
/// <seealso cref="OrdinalAttribute"/>
[global::System.AttributeUsage(global::System.AttributeTargets.Parameter | global::System.AttributeTargets.Property | global::System.AttributeTargets.Field, AllowMultiple = false)]
#if !NET20 && !NET35 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6 && !NETCOREAPP1_0 && !NETCOREAPP1_1
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
internal class TypeAttribute : global::System.Attribute
{
/// <summary>
/// Creates an attribute instance.
/// </summary>
/// <param name="type">The injection type. See also <see cref="IConfiguration.Bind{T}"/> and <see cref="IBinding.Bind{T}"/>.</param>
public TypeAttribute(global::System.Type type) { }
}
/// <summary>
/// Indicates that a property or method can be automatically added as a binding.
/// <example>
/// <code>
/// internal class DependencyProvider
/// {
/// [Bind()]
/// public Dependency Dep => new Dependency();
/// }
/// </code>
/// <code>
/// internal class DependencyProvider
/// {
/// [Bind(typeof(IDependency<TT>), Lifetime.Singleton)]
/// public Dependency GetDep<T>() => new Dependency();
/// }
/// </code>
/// <code>
/// internal class DependencyProvider
/// {
/// [Bind(typeof(IDependency), Lifetime.PerResolve, "some tag")]
/// public Dependency GetDep(int id) => new Dependency(id);
/// }
/// </code>
/// </example>
/// </summary>
[global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Method | global::System.AttributeTargets.Field)]
#if !NET20 && !NET35 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6 && !NETCOREAPP1_0 && !NETCOREAPP1_1
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
internal class BindAttribute : global::System.Attribute
{
/// <summary>
/// Creates an attribute instance.
/// </summary>
public BindAttribute(global::System.Type type = default(global::System.Type), Lifetime lifetime = Lifetime.Transient, params object[] tags) { }
}
/// <summary>
/// Determines how the partial class will be generated. The <see cref="DI.Setup"/> method has an additional argument <c>kind</c>, which defines the type of composition:
/// <example>
/// <code>
/// DI.Setup("BaseComposition", CompositionKind.Internal);
/// </code>
/// </example>
/// </summary>
/// <seealso cref="DI.Setup"/>
internal enum CompositionKind
{
/// <summary>
/// This value is used by default. If this value is specified, a normal partial class will be generated.
/// </summary>
Public,
/// <summary>
/// If this value is specified, the class will not be generated, but this setting can be used by other users as a baseline. The API call <see cref="IConfiguration.DependsOn"/> is mandatory.
/// </summary>
Internal,
/// <summary>
/// No partial classes will be created when this value is specified, but this setting is the baseline for all installations in the current project, and the API call <see cref="IConfiguration.DependsOn"/> is not required.
/// </summary>
Global
}
/// <summary>
/// Determines a kind of root of the composition.
/// </summary>
/// <seealso cref="IConfiguration.Root{T}"/>
[global::System.Flags]
internal enum RootKinds
{
/// <summary>
/// Specifies to use the default composition root kind.
/// </summary>
Default = RootKinds.Public | RootKinds.Property,
/// <summary>
/// Specifies to use a <c>public</c> access modifier for the root of the composition.
/// </summary>
Public = 1,
/// <summary>
/// Specifies to use a <c>internal</c> access modifier for the root of the composition.
/// </summary>
Internal = 1 << 1,
/// <summary>
/// Specifies to use a <c>private</c> access modifier for the root of the composition.
/// </summary>
Private = 1 << 2,
/// <summary>
/// Specifies to create a composition root as a property.
/// </summary>
Property = 1 << 3,
/// <summary>
/// Specifies to create a composition root as a method.
/// </summary>
Method = 1 << 4,
/// <summary>
/// Specifies to create a static root of the composition.
/// </summary>
Static = 1 << 5,
/// <summary>
/// Specifies to create a partial root of the composition.
/// </summary>
Partial = 1 << 6,
/// <summary>
/// Specifies to create a exposed root of the composition.
/// </summary>
Exposed = 1 << 7,
/// <summary>
/// Specifies to use a <c>protected</c> access modifier for the root of the composition.
/// </summary>
Protected = 1 << 8,
}
/// <summary>
/// Represents well known tags.
/// </summary>
#if !NET20 && !NET35 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6 && !NETCOREAPP1_0 && !NETCOREAPP1_1
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
internal partial class Tag
{
private static readonly Tag Shared = new Tag();
/// <summary>
/// Unique tag.
/// Begins the definition of the binding.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind<IService>(Tag.Unique).To<Service1>()
/// .Bind<IService>(Tag.Unique).To<Service1>()
/// .Root<IEnumerable<IService>>("Root");
/// </code>
/// </example>
/// </summary>
public static readonly Tag Unique = Shared;
/// <summary>
/// Tag of target implementation type.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind<IService>(Tag.Type).To<Service>()
/// .Root<IService>("Root", typeof(Service));
/// </code>
/// </example>
/// </summary>
public static readonly Tag Type = Shared;
/// <summary>
/// This tag allows you to determine which binding will be used for explicit injection for a particular injection site.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind(Tag.On("MyNamespace.Service.Service:dep"))
/// .To<Dependency>()
/// .Bind().To<Service>()
/// .Root<IService>("Root");
/// </code>
/// </example>
/// </summary>
/// <param name="injectionSites">Set of labels for inection each, must be specified in a special format: <namespace>.<type>.<member>[:argument]. The argument is specified only for the constructor and methods. The wildcards '*' and '?' are supported. All names are case-sensitive. The global namespace prefix 'global::' must be omitted.</param>
public static Tag On(params string[] injectionSites) => Shared;
/// <summary>
/// This tag allows you to determine which binding will be used for explicit injection for a particular constructor argument.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind(Tag.OnConstructorArg<Service>("dep"))
/// .To<Dependency>()
/// .Bind().To<Service>()
/// .Root<IService>("Root");
/// </code>
/// </example>
/// </summary>
/// <param name="argName">The name of the constructor argument.</param>
public static Tag OnConstructorArg<T>(string argName) => Shared;
/// <summary>
/// This tag allows you to define which binding will be used for explicit injection for property or field of the type.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind(Tag.OnMember<Service>("DepProperty"))
/// .To<Dependency>()
/// .Bind().To<Service>()
/// .Root<IService>("Root");
/// </code>
/// </example>
/// </summary>
/// <param name="memberName">The name of the type member.</param>
public static Tag OnMember<T>(string memberName) => Shared;
/// <summary>
/// This tag allows you to determine which binding will be used for explicit injection for a particular method argument.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind(Tag.OnMethodArg<Service>("DoSomething", "state"))
/// .To<Dependency>()
/// .Bind().To<Service>()
/// .Root<IService>("Root");
/// </code>
/// </example>
/// </summary>
/// <param name="methodName">The name of the type method.</param>
/// <param name="argName">The name of the method argument.</param>
public static Tag OnMethodArg<T>(string methodName, string argName) => Shared;
}
/// <summary>
/// This abstraction allows a disposable object to be disposed of.
/// </summary>
internal interface IOwned
: global::System.IDisposable
#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
, global::System.IAsyncDisposable
#endif
{
}
/// <summary>
/// Performs accumulation and disposal of disposable objects.
/// </summary>
#if !NET20 && !NET35 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6 && !NETCOREAPP1_0 && !NETCOREAPP1_1
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
internal sealed partial class Owned: global::System.Collections.Generic.List<object>, global::Pure.DI.IOwned
{
private volatile bool _isDisposed;
/// <inheritdoc />
public void Dispose()
{
if (_isDisposed)
{
return;
}
_isDisposed = true;
try
{
for (var i = Count - 1; i >= 0; i--)
{
switch (this[i])
{
case global::Pure.DI.IOwned _:
break;
case global::System.IDisposable disposableInstance:
try
{
disposableInstance.Dispose();
}
catch (global::System.Exception exception)
{
OnDisposeException(disposableInstance, exception);
}
break;
#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
case global::System.IAsyncDisposable asyncDisposableInstance:
try
{
var valueTask = asyncDisposableInstance.DisposeAsync();
if (!valueTask.IsCompleted)
{
valueTask.AsTask().Wait();
}
}
catch (global::System.Exception exception)
{
OnDisposeAsyncException(asyncDisposableInstance, exception);
}
break;
#endif
}
}
}
finally
{
Clear();
}
}
#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
/// <inheritdoc />
public async global::System.Threading.Tasks.ValueTask DisposeAsync()
{
if (_isDisposed)
{
return;
}
_isDisposed = true;
try
{
for (var i = Count - 1; i >= 0; i--)
{
switch (this[i])
{
case global::Pure.DI.IOwned _:
break;
case global::System.IAsyncDisposable asyncDisposableInstance:
try
{
await asyncDisposableInstance.DisposeAsync();
}
catch (global::System.Exception exception)
{
OnDisposeAsyncException(asyncDisposableInstance, exception);
}
break;
case global::System.IDisposable disposableInstance:
try
{
disposableInstance.Dispose();
}
catch (global::System.Exception exception)
{
OnDisposeException(disposableInstance, exception);
}
break;
}
}
}
finally
{
Clear();
}
}
#endif
/// <summary>
/// Implement this partial method to handle the exception on disposing.
/// </summary>
/// <param name="disposableInstance">The disposable instance.</param>
/// <param name="exception">Exception occurring during disposal.</param>
/// <typeparam name="T">The actual type of instance being disposed of.</typeparam>
partial void OnDisposeException<T>(T disposableInstance, global::System.Exception exception)
where T : global::System.IDisposable;
#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
/// <summary>
/// Implement this partial method to handle the exception on disposing.
/// </summary>
/// <param name="asyncDisposableInstance">The disposable instance.</param>
/// <param name="exception">Exception occurring during disposal.</param>
/// <typeparam name="T">The actual type of instance being disposed of.</typeparam>
partial void OnDisposeAsyncException<T>(T asyncDisposableInstance, global::System.Exception exception)
where T : global::System.IAsyncDisposable;
#endif
}
/// <summary>
/// Contains a value and gives the ability to dispose of that value.
/// </summary>
/// <typeparam name="T">Type of value owned.</typeparam>
[global::System.Diagnostics.DebuggerDisplay("{Value}")]
[global::System.Diagnostics.DebuggerTypeProxy(typeof(global::Pure.DI.Owned<>.DebugView))]
internal readonly struct Owned<T>: global::Pure.DI.IOwned
{
/// <summary>
/// Own value.
/// </summary>
public readonly T Value;
private readonly global::Pure.DI.IOwned _owned;
/// <summary>
/// Creates a new instance.
/// </summary>
/// <param name="value">Own value.</param>
/// <param name="owned">The abstraction allows a disposable object to be disposed of.</param>
public Owned(T value, global::Pure.DI.IOwned owned)
{
Value = value;
_owned = owned;
}
/// <inheritdoc />
public void Dispose()
{
_owned.Dispose();
}
#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
/// <inheritdoc />
public global::System.Threading.Tasks.ValueTask DisposeAsync()
{
return _owned.DisposeAsync();
}
#endif
#if !NET20 && !NET35 && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6 && !NETCOREAPP1_0 && !NETCOREAPP1_1
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
private class DebugView
{
private readonly global::Pure.DI.Owned<T> _owned;
public DebugView(global::Pure.DI.Owned<T> owned)
{
_owned = owned;
}
public T Value
{
get { return _owned.Value; }
}
[global::System.Diagnostics.DebuggerBrowsable(global::System.Diagnostics.DebuggerBrowsableState.Collapsed)]
public global::Pure.DI.IOwned Owned
{
get { return _owned._owned; }
}
}
}
/// <summary>
/// An API for a Dependency Injection setup.
/// </summary>
/// <seealso cref="DI.Setup"/>
internal interface IConfiguration
{
/// <summary>
/// Begins the binding definition for the implementation type itself, and if the implementation is not an abstract class or structure, for all abstract but NOT special types that are directly implemented.
/// Special types include:
/// <list type="bullet">
/// <item>System.Object</item>
/// <item>System.Enum</item>
/// <item>System.MulticastDelegate</item>
/// <item>System.Delegate</item>
/// <item>System.Collections.IEnumerable</item>
/// <item>System.Collections.Generic.IEnumerable<T></item>
/// <item>System.Collections.Generic.IList<T></item>
/// <item>System.Collections.Generic.ICollection<T></item>
/// <item>System.Collections.IEnumerator</item>
/// <item>System.Collections.Generic.IEnumerator<T></item>
/// <item>System.Collections.Generic.IIReadOnlyList<T></item>
/// <item>System.Collections.Generic.IReadOnlyCollection<T></item>
/// <item>System.IDisposable</item>
/// <item>System.IAsyncResult</item>
/// <item>System.AsyncCallback</item>
/// </list>
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind().To<Service>();
/// </code>
/// </example>
/// </summary>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding.</param>
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="To{T}()"/>
/// <seealso cref="To{T}(System.Func{Pure.DI.IContext,T})"/>
/// <seealso cref="To{T1,T}()"/>
/// <seealso cref="To{T1,T2,T}()"/>
/// <seealso cref="Tags"/>
/// <seealso cref="As"/>
IBinding Bind(params object[] tags);
/// <summary>
/// Begins the definition of the binding.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .Bind<IService>().To<Service>();
/// </code>
/// </example>
/// </summary>
/// <typeparam name="T">The type of dependency to be bound.</typeparam>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding.</param>
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="To{T}()"/>
/// <seealso cref="To{T}(System.Func{Pure.DI.IContext,T})"/>
/// <seealso cref="To{T1,T}()"/>
/// <seealso cref="To{T1,T2,T}()"/>
/// <seealso cref="Tags"/>
/// <seealso cref="As"/>
IBinding Bind<T>(params object[] tags);
/// <summary>
/// Begins binding definition for multiple dependencies. See <see cref="Bind{T}"/> for examples.
/// </summary>
/// <typeparam name="T1">The type 1 of dependency to be bound.</typeparam>
/// <typeparam name="T2">The type 2 of dependency to be bound.</typeparam>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding.</param>
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="To{T}()"/>
/// <seealso cref="To{T}(System.Func{Pure.DI.IContext,T})"/>
/// <seealso cref="To{T1,T}()"/>
/// <seealso cref="To{T1,T2,T}()"/>
/// <seealso cref="Tags"/>
/// <seealso cref="As"/>
IBinding Bind<T1, T2>(params object[] tags);
/// <summary>
/// Begins binding definition for multiple dependencies. See <see cref="Bind{T}"/> for examples.
/// </summary>
/// <typeparam name="T1">The type 1 of dependency to be bound.</typeparam>
/// <typeparam name="T2">The type 2 of dependency to be bound.</typeparam>
/// <typeparam name="T3">The type 3 of dependency to be bound.</typeparam>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding.</param>
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="To{T}()"/>
/// <seealso cref="To{T}(System.Func{Pure.DI.IContext,T})"/>
/// <seealso cref="To{T1,T}()"/>
/// <seealso cref="To{T1,T2,T}()"/>
/// <seealso cref="Tags"/>
/// <seealso cref="As"/>
IBinding Bind<T1, T2, T3>(params object[] tags);
/// <summary>
/// Begins binding definition for multiple dependencies. See <see cref="Bind{T}"/> for examples.
/// </summary>
/// <typeparam name="T1">The type 1 of dependency to be bound.</typeparam>
/// <typeparam name="T2">The type 2 of dependency to be bound.</typeparam>
/// <typeparam name="T3">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 4 of dependency to be bound.</typeparam>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding.</param>
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="To{T}()"/>
/// <seealso cref="To{T}(System.Func{Pure.DI.IContext,T})"/>
/// <seealso cref="To{T1,T}()"/>
/// <seealso cref="To{T1,T2,T}()"/>
/// <seealso cref="Tags"/>
/// <seealso cref="As"/>
IBinding Bind<T1, T2, T3, T4>(params object[] tags);
/// <summary>
/// Begins binding definition for multiple dependencies. See <see cref="Bind{T}"/> for examples.
/// </summary>
/// <typeparam name="T1">The type 1 of dependency to be bound.</typeparam>
/// <typeparam name="T2">The type 2 of dependency to be bound.</typeparam>
/// <typeparam name="T3">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 4 of dependency to be bound.</typeparam>
/// <typeparam name="T5">The type 5 of dependency to be bound.</typeparam>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding.</param>
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="To{T}()"/>
/// <seealso cref="To{T}(System.Func{Pure.DI.IContext,T})"/>
/// <seealso cref="To{T1,T}()"/>
/// <seealso cref="To{T1,T2,T}()"/>
/// <seealso cref="Tags"/>
/// <seealso cref="As"/>
IBinding Bind<T1, T2, T3, T4, T5>(params object[] tags);
/// <summary>
/// Begins binding definition for multiple dependencies. See <see cref="Bind{T}"/> for examples.
/// </summary>
/// <typeparam name="T1">The type 1 of dependency to be bound.</typeparam>
/// <typeparam name="T2">The type 2 of dependency to be bound.</typeparam>
/// <typeparam name="T3">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 4 of dependency to be bound.</typeparam>
/// <typeparam name="T5">The type 5 of dependency to be bound.</typeparam>
/// <typeparam name="T6">The type 6 of dependency to be bound.</typeparam>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding.</param>
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="To{T}()"/>
/// <seealso cref="To{T}(System.Func{Pure.DI.IContext,T})"/>
/// <seealso cref="To{T1,T}()"/>
/// <seealso cref="To{T1,T2,T}()"/>
/// <seealso cref="Tags"/>
/// <seealso cref="As"/>
IBinding Bind<T1, T2, T3, T4, T5, T6>(params object[] tags);
/// <summary>
/// Begins binding definition for multiple dependencies. See <see cref="Bind{T}"/> for examples.
/// </summary>
/// <typeparam name="T1">The type 1 of dependency to be bound.</typeparam>
/// <typeparam name="T2">The type 2 of dependency to be bound.</typeparam>
/// <typeparam name="T3">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 4 of dependency to be bound.</typeparam>
/// <typeparam name="T5">The type 5 of dependency to be bound.</typeparam>
/// <typeparam name="T6">The type 6 of dependency to be bound.</typeparam>
/// <typeparam name="T7">The type 7 of dependency to be bound.</typeparam>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding.</param>
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="To{T}()"/>
/// <seealso cref="To{T}(System.Func{Pure.DI.IContext,T})"/>
/// <seealso cref="To{T1,T}()"/>
/// <seealso cref="To{T1,T2,T}()"/>
/// <seealso cref="Tags"/>
/// <seealso cref="As"/>
IBinding Bind<T1, T2, T3, T4, T5, T6, T7>(params object[] tags);
/// <summary>
/// Begins binding definition for multiple dependencies. See <see cref="Bind{T}"/> for examples.
/// </summary>
/// <typeparam name="T1">The type 1 of dependency to be bound.</typeparam>
/// <typeparam name="T2">The type 2 of dependency to be bound.</typeparam>
/// <typeparam name="T3">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 4 of dependency to be bound.</typeparam>
/// <typeparam name="T5">The type 5 of dependency to be bound.</typeparam>
/// <typeparam name="T6">The type 6 of dependency to be bound.</typeparam>
/// <typeparam name="T7">The type 7 of dependency to be bound.</typeparam>
/// <typeparam name="T8">The type 8 of dependency to be bound.</typeparam>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding.</param>
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="To{T}()"/>
/// <seealso cref="To{T}(System.Func{Pure.DI.IContext,T})"/>
/// <seealso cref="To{T1,T}()"/>
/// <seealso cref="To{T1,T2,T}()"/>
/// <seealso cref="Tags"/>
/// <seealso cref="As"/>
IBinding Bind<T1, T2, T3, T4, T5, T6, T7, T8>(params object[] tags);
/// <summary>
/// Begins the definition of the binding with <see cref="Root{T}"/> applied.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .RootBind<IService>();
/// </code>
/// </example>
/// </summary>
/// <typeparam name="T">The type of dependency to be bound.</typeparam>
/// <param name="name">Specifies the unique name of the root of the composition. If the value is empty, a private root will be created, which can be used when calling <c>Resolve</c> methods.</param>
/// <param name="kind">The Optional argument specifying the kind for the root of the Composition.</param>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding. If is is not empty, the first tag is used for the root.</param>
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="To{T}()"/>
/// <seealso cref="To{T}(System.Func{Pure.DI.IContext,T})"/>
/// <seealso cref="To{T1,T}()"/>
/// <seealso cref="To{T1,T2,T}()"/>
/// <seealso cref="Tags"/>
/// <seealso cref="As"/>
IBinding RootBind<T>(string name = "", RootKinds kind = RootKinds.Default, params object[] tags);
/// <summary>
/// Indicates the use of some single or multiple setups as base setups by name.
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .DependsOn(nameof(CompositionBase));
/// </code>
/// </example>
/// </summary>
/// <param name="setupNames">A set of names for the basic setups on which this one depends.</param>
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="DI.Setup"/>
IConfiguration DependsOn(params string[] setupNames);
/// <summary>
/// Specifies a custom generic type argument attribute.
/// <example>
/// <code>
/// [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)]
/// class MyGenericTypeArgumentAttribute : Attribute;
///
/// [MyGenericTypeArgument]
/// interface TTMy;
///