Func helps when the logic must enter instances of some type on demand or more than once. This is a very handy mechanism for instance replication. For example it is used when implementing the Lazy<T> injection.
using Shouldly;
using Pure.DI;
using System.Collections.Immutable;
DI.Setup(nameof(Composition))
.Bind().As(Lifetime.Singleton).To<TicketIdGenerator>()
.Bind().To<Ticket>()
.Bind().To<QueueTerminal>()
// Composition root
.Root<IQueueTerminal>("Terminal");
var composition = new Composition();
var terminal = composition.Terminal;
terminal.Tickets.Length.ShouldBe(3);
terminal.Tickets[0].Id.ShouldBe(1);
terminal.Tickets[1].Id.ShouldBe(2);
terminal.Tickets[2].Id.ShouldBe(3);
interface ITicketIdGenerator
{
int NextId { get; }
}
class TicketIdGenerator : ITicketIdGenerator
{
public int NextId => ++field;
}
interface ITicket
{
int Id { get; }
}
class Ticket(ITicketIdGenerator idGenerator) : ITicket
{
public int Id { get; } = idGenerator.NextId;
}
interface IQueueTerminal
{
ImmutableArray<ITicket> Tickets { get; }
}
class QueueTerminal(Func<ITicket> ticketFactory) : IQueueTerminal
{
public ImmutableArray<ITicket> Tickets { get; } =
[
// The factory creates a new instance of the ticket each time it is called
ticketFactory(),
ticketFactory(),
ticketFactory()
];
}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 runBe careful, replication takes into account the lifetime of the object.
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 TicketIdGenerator? _singletonTicketIdGenerator62;
public IQueueTerminal Terminal
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
Func<ITicket> perBlockFunc400 = new Func<ITicket>(
[MethodImpl(MethodImplOptions.AggressiveInlining)]
() =>
{
if (_singletonTicketIdGenerator62 is null)
lock (_lock)
if (_singletonTicketIdGenerator62 is null)
{
_singletonTicketIdGenerator62 = new TicketIdGenerator();
}
return new Ticket(_singletonTicketIdGenerator62);
});
return new QueueTerminal(perBlockFunc400);
}
}
}Class diagram:
---
config:
maxTextSize: 2147483647
maxEdges: 2147483647
class:
hideEmptyMembersBox: true
---
classDiagram
TicketIdGenerator --|> ITicketIdGenerator
Ticket --|> ITicket
QueueTerminal --|> IQueueTerminal
Composition ..> QueueTerminal : IQueueTerminal Terminal
Ticket o-- "Singleton" TicketIdGenerator : ITicketIdGenerator
QueueTerminal o-- "PerBlock" FuncᐸITicketᐳ : FuncᐸITicketᐳ
FuncᐸITicketᐳ *-- Ticket : ITicket
namespace Pure.DI.UsageTests.BCL.FuncScenario {
class Composition {
<<partial>>
+IQueueTerminal Terminal
}
class IQueueTerminal {
<<interface>>
}
class ITicket {
<<interface>>
}
class ITicketIdGenerator {
<<interface>>
}
class QueueTerminal {
<<class>>
+QueueTerminal(FuncᐸITicketᐳ ticketFactory)
}
class Ticket {
<<class>>
+Ticket(ITicketIdGenerator idGenerator)
}
class TicketIdGenerator {
<<class>>
+TicketIdGenerator()
}
}
namespace System {
class FuncᐸITicketᐳ {
<<delegate>>
}
}