This example creates dependencies on demand using a factory delegate. The service (GameLevel) needs multiple instances of IEnemy, so it receives a Func<IEnemy> that can create new instances when needed.
This approach is useful when instances are created lazily or repeatedly during business execution.
using Shouldly;
using Pure.DI;
using System.Collections.Generic;
DI.Setup(nameof(Composition))
.Bind().To<Enemy>()
.Bind().To<GameLevel>()
// Composition root
.Root<IGameLevel>("GameLevel");
var composition = new Composition();
var gameLevel = composition.GameLevel;
// Verifies that two distinct enemies have been spawned
gameLevel.Enemies.Count.ShouldBe(2);
// Represents a game entity that acts as an enemy
interface IEnemy;
class Enemy : IEnemy;
// Represents a game level that manages entities
interface IGameLevel
{
IReadOnlyList<IEnemy> Enemies { get; }
}
class GameLevel(Func<IEnemy> enemySpawner) : IGameLevel
{
// The factory spawns a fresh enemy instance on each call.
public IReadOnlyList<IEnemy> Enemies { get; } =
[
enemySpawner(),
enemySpawner()
];
}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 runKey elements:
Enemyis bound to theIEnemyinterface, andGameLevelis bound toIGameLevel.- The
GameLevelconstructor acceptsFunc<IEnemy>, enabling deferred creation of entities. - The
GameLevelcalls the factory twice, resulting in two distinctEnemyinstances stored in itsEnemiescollection.
This approach lets factories control lifetime and instantiation timing. Pure.DI resolves a new IEnemy each time the factory is invoked.
Limitations: factory delegate calls can create many objects, so lifetime choices still matter for performance and state.
Common pitfalls:
- Assuming
Func<T>always returns new instances regardless of configured lifetime. - Hiding expensive work behind repeated on-demand calls. See also: Injections on demand with arguments, Func.
The following partial class will be generated:
partial class Composition
{
public IGameLevel GameLevel
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
Func<IEnemy> perBlockFunc296 = new Func<IEnemy>(
[MethodImpl(MethodImplOptions.AggressiveInlining)]
() =>
{
return new Enemy();
});
return new GameLevel(perBlockFunc296);
}
}
}Class diagram:
---
config:
maxTextSize: 2147483647
maxEdges: 2147483647
class:
hideEmptyMembersBox: true
---
classDiagram
Enemy --|> IEnemy
GameLevel --|> IGameLevel
Composition ..> GameLevel : IGameLevel GameLevel
GameLevel o-- "PerBlock" FuncᐸIEnemyᐳ : FuncᐸIEnemyᐳ
FuncᐸIEnemyᐳ *-- Enemy : IEnemy
namespace Pure.DI.UsageTests.Basics.InjectionOnDemandScenario {
class Composition {
<<partial>>
+IGameLevel GameLevel
}
class Enemy {
<<class>>
+Enemy()
}
class GameLevel {
<<class>>
+GameLevel(FuncᐸIEnemyᐳ enemySpawner)
}
class IEnemy {
<<interface>>
}
class IGameLevel {
<<interface>>
}
}
namespace System {
class FuncᐸIEnemyᐳ {
<<delegate>>
}
}