For example, if a certain lifetime is used more often than others, you can make it the default lifetime for a certain type:
using Shouldly;
using Pure.DI;
using static Pure.DI.Lifetime;
DI.Setup(nameof(Composition))
// In a real base station, the time source (PTP/GNSS disciplined clock)
// is a shared infrastructure component:
// it should be created once per station and reused everywhere.
.DefaultLifetime<ITimeSource>(Singleton)
// Time source used by multiple subsystems
.Bind().To<GnssTimeSource>()
// Upper-level station components (usually transient by default)
.Bind().To<BaseStationController>()
.Bind().To<RadioScheduler>()
// Composition root (represents "get me a controller instance")
.Root<IBaseStationController>("Controller");
var composition = new Composition();
// Two independent controller instances (e.g., two independent operations)
var controller1 = composition.Controller;
var controller2 = composition.Controller;
controller1.ShouldNotBe(controller2);
// Inside one controller we request ITimeSource twice:
// the same singleton instance should be injected both times.
controller1.SyncTimeSource.ShouldBe(controller1.SchedulerTimeSource);
// Across different controllers the same station-wide time source is reused.
controller1.SyncTimeSource.ShouldBe(controller2.SyncTimeSource);
// A shared station-wide dependency
interface ITimeSource
{
long UnixTimeMilliseconds { get; }
}
// Represents a GNSS-disciplined clock (or PTP grandmaster input).
// In real deployments you'd talk to a driver / NIC / daemon here.
class GnssTimeSource : ITimeSource
{
public long UnixTimeMilliseconds => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
interface IBaseStationController
{
ITimeSource SyncTimeSource { get; }
ITimeSource SchedulerTimeSource { get; }
}
// A "top-level" controller of the base station.
// It depends on the time source for synchronization and for scheduling decisions.
class BaseStationController(
ITimeSource syncTimeSource,
RadioScheduler scheduler)
: IBaseStationController
{
// Used for time synchronization / frame timing
public ITimeSource SyncTimeSource { get; } = syncTimeSource;
// Demonstrates that scheduler also uses the same singleton time source
public ITimeSource SchedulerTimeSource { get; } = scheduler.TimeSource;
}
// A subsystem (e.g., MAC scheduler) that also needs precise time.
class RadioScheduler(ITimeSource timeSource)
{
public ITimeSource TimeSource { get; } = timeSource;
}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 runNote
Setting default lifetime for types simplifies configuration when the same lifetime is consistently applied.
The following partial class will be generated:
partial class Composition
{
#if NET9_0_OR_GREATER
private readonly Lock _lock = new Lock();
#else
private readonly Object _lock = new Object();
#endif
private GnssTimeSource? _singletonGnssTimeSource62;
public IBaseStationController Controller
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
EnsureGnssTimeSourceExists();
return new BaseStationController(_singletonGnssTimeSource62, new RadioScheduler(_singletonGnssTimeSource62));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void EnsureGnssTimeSourceExists()
{
if (_singletonGnssTimeSource62 is null)
lock (_lock)
if (_singletonGnssTimeSource62 is null)
{
_singletonGnssTimeSource62 = new GnssTimeSource();
}
}
}
}
}Class diagram:
---
config:
maxTextSize: 2147483647
maxEdges: 2147483647
class:
hideEmptyMembersBox: true
---
classDiagram
GnssTimeSource --|> ITimeSource
BaseStationController --|> IBaseStationController
Composition ..> BaseStationController : IBaseStationController Controller
BaseStationController o-- "Singleton" GnssTimeSource : ITimeSource
BaseStationController *-- RadioScheduler : RadioScheduler
RadioScheduler o-- "Singleton" GnssTimeSource : ITimeSource
namespace Pure.DI.UsageTests.Lifetimes.DefaultLifetimeForTypeScenario {
class BaseStationController {
<<class>>
+BaseStationController(ITimeSource syncTimeSource, RadioScheduler scheduler)
}
class Composition {
<<partial>>
+IBaseStationController Controller
}
class GnssTimeSource {
<<class>>
+GnssTimeSource()
}
class IBaseStationController {
<<interface>>
}
class ITimeSource {
<<interface>>
}
class RadioScheduler {
<<class>>
+RadioScheduler(ITimeSource timeSource)
}
}