func-with-arguments.md
July 5, 2026 · View on GitHub
Func with arguments
Sometimes an instance can only be created with values known at runtime, such as an id or a name. Injecting a Func<..., T> with arguments gives you a factory: values passed at call time are matched by type to the constructor parameters (here int id and string name of Person), while the remaining dependencies, like IClock, are resolved from the composition as usual.
using Shouldly;
using Pure.DI;
using System.Collections.Immutable;
DI.Setup(nameof(Composition))
.Bind().As(Lifetime.Singleton).To<Clock>()
.Bind().To<Person>()
.Bind().To<Team>()
// Composition root
.Root<ITeam>("Team");
var composition = new Composition();
var team = composition.Team;
team.Members.Length.ShouldBe(3);
team.Members[0].Id.ShouldBe(10);
team.Members[0].Name.ShouldBe("Nik");
team.Members[1].Id.ShouldBe(20);
team.Members[1].Name.ShouldBe("Mike");
team.Members[2].Id.ShouldBe(30);
team.Members[2].Name.ShouldBe("Jake");
interface IClock
{
DateTimeOffset Now { get; }
}
class Clock : IClock
{
public DateTimeOffset Now => DateTimeOffset.Now;
}
interface IPerson
{
int Id { get; }
string Name { get; }
}
class Person(string name, IClock clock, int id)
: IPerson
{
public int Id => id;
public string Name => name;
}
interface ITeam
{
ImmutableArray<IPerson> Members { get; }
}
class Team(Func<int, string, IPerson> personFactory) : ITeam
{
public ImmutableArray<IPerson> Members { get; } =
[
personFactory(10, "Nik"),
personFactory(20, "Mike"),
personFactory(30, "Jake")
];
}
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 Sample
dotnet 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 run
Note
Func with arguments provides flexibility for scenarios where you need to pass runtime parameters during instance creation.
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 Clock? _singletonCompositionInOtherProject;
public ITeam Team
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
Func<int, string, IPerson> perBlockFuncInt32StringIPerson;
// Creates a factory with runtime arguments
Func<int, string, IPerson> localFactory = new Func<int, string, IPerson>((int localArg11, string localArg2) =>
{
// Creates the result
int overriddenInt32 = localArg11;
string overriddenString = localArg2;
if (_singletonCompositionInOtherProject is null)
lock (_lock)
if (_singletonCompositionInOtherProject is null)
{
_singletonCompositionInOtherProject = new Clock();
}
return new Person(overriddenString, _singletonCompositionInOtherProject, overriddenInt32);
});
perBlockFuncInt32StringIPerson = localFactory;
return new Team(perBlockFuncInt32StringIPerson);
}
}
}
Class diagram:
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
Person --|> IPerson
Team --|> ITeam
Composition ..> Team : ITeam Team
Person o-- "Singleton" Clock : IClock
Person *-- Int32 : Int32
Person *-- String : String
Team o-- "PerBlock" FuncᐸInt32ˏStringˏIPersonᐳ : FuncᐸInt32ˏStringˏIPersonᐳ
FuncᐸInt32ˏStringˏIPersonᐳ *-- Person : IPerson
namespace Pure.DI.UsageTests.BCL.FuncWithArgumentsScenario {
class Clock {
<<class>>
}
class Composition {
<<partial>>
+ITeam Team
}
class IPerson {
<<interface>>
}
class ITeam {
<<interface>>
}
class Person {
<<class>>
+Person(String name, IClock clock, Int32 id)
}
class Team {
<<class>>
+Team(FuncᐸInt32ˏStringˏIPersonᐳ personFactory)
}
}
namespace System {
class FuncᐸInt32ˏStringˏIPersonᐳ {
<<delegate>>
}
class Int32 {
<<struct>>
}
class String {
<<class>>
}
}
See also: