The Transient lifetime specifies to create a new dependency instance each time. It is the default lifetime and can be omitted.
using Shouldly;
using Pure.DI;
using static Pure.DI.Lifetime;
DI.Setup(nameof(Composition))
.Bind().As(Transient).To<Buffer>()
.Bind().To<BatchProcessor>()
.Root<IBatchProcessor>("Processor");
var composition = new Composition();
var processor = composition.Processor;
// Verify that input and output buffers are different instances.
// This is critical for the batch processor to avoid data corruption
// during reading. The Transient lifetime ensures a new instance
// is created for each dependency injection.
processor.Input.ShouldNotBe(processor.Output);
// Represents a memory buffer that should be unique for each operation
interface IBuffer;
class Buffer : IBuffer;
interface IBatchProcessor
{
public IBuffer Input { get; }
public IBuffer Output { get; }
}
class BatchProcessor(
IBuffer input,
IBuffer output)
: IBatchProcessor
{
public IBuffer Input { get; } = input;
public IBuffer Output { get; } = output;
}Running this code sample locally
- Make sure you have the .NET SDK 10.0 or later installed
dotnet --list-sdk- Create a net10.0 (or later) console application
dotnet new console -n Sampledotnet add package Pure.DI
dotnet add package Shouldly- Copy the example code into the Program.cs file
You are ready to run the example 🚀
dotnet runThe Transient lifetime is the safest and is used by default. Yes, its widespread use can cause a lot of memory traffic, but if there are doubts about thread safety, the Transient lifetime is preferable because each consumer has its own instance of the dependency. The following nuances should be considered when choosing the Transient lifetime:
-
There will be unnecessary memory overhead that could be avoided.
-
Every object created must be disposed of, and this will waste CPU resources, at least when the GC does its memory-clearing job.
-
Poorly designed constructors can run slowly, perform functions that are not their own, and greatly hinder the efficient creation of compositions of multiple objects.
Important
The following very important rule, in my opinion, will help in the last point. Now, when a constructor is used to implement dependencies, it should not be loaded with other tasks. Accordingly, constructors should be free of all logic except for checking arguments and saving them for later use. Following this rule, even the largest compositions of objects will be built quickly.
The following partial class will be generated:
partial class Composition
{
public IBatchProcessor Processor
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return new BatchProcessor(new Buffer(), new Buffer());
}
}
}Class diagram:
---
config:
maxTextSize: 2147483647
maxEdges: 2147483647
class:
hideEmptyMembersBox: true
---
classDiagram
Buffer --|> IBuffer
BatchProcessor --|> IBatchProcessor
Composition ..> BatchProcessor : IBatchProcessor Processor
BatchProcessor *-- "2 " Buffer : IBuffer
namespace Pure.DI.UsageTests.Lifetimes.TransientScenario {
class BatchProcessor {
<<class>>
+BatchProcessor(IBuffer input, IBuffer output)
}
class Buffer {
<<class>>
+Buffer()
}
class Composition {
<<partial>>
+IBatchProcessor Processor
}
class IBatchProcessor {
<<interface>>
}
class IBuffer {
<<interface>>
}
}