This is the recommended model for production code: depend on abstractions and bind them to implementations in composition. It keeps business code independent from infrastructure details and makes replacements predictable.
using Pure.DI;
DI.Setup(nameof(Composition))
// Binding abstractions to their implementations:
// The interface IGpsSensor is bound to the implementation GpsSensor
.Bind<IGpsSensor>().To<GpsSensor>()
// The interface INavigationSystem is bound to the implementation NavigationSystem
.Bind<INavigationSystem>().To<NavigationSystem>()
// Specifies to create a composition root
// of type "VehicleComputer" with the name "VehicleComputer"
.Root<VehicleComputer>("VehicleComputer");
var composition = new Composition();
// Usage:
// var vehicleComputer = new VehicleComputer(new NavigationSystem(new GpsSensor()));
var vehicleComputer = composition.VehicleComputer;
vehicleComputer.StartTrip();
// The sensor abstraction
interface IGpsSensor;
// The sensor implementation
class GpsSensor : IGpsSensor;
// The service abstraction
interface INavigationSystem
{
void Navigate();
}
// The service implementation
class NavigationSystem(IGpsSensor sensor) : INavigationSystem
{
public void Navigate()
{
// Navigation logic using the sensor...
}
}
// The consumer of the abstraction
partial class VehicleComputer(INavigationSystem navigationSystem)
{
public void StartTrip() => navigationSystem.Navigate();
}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- Add a reference to the NuGet package
dotnet add package Pure.DI- Copy the example code into the Program.cs file
You are ready to run the example 🚀
dotnet runThe binding chain maps abstractions to concrete types so the generator can build a fully concrete object graph. This keeps consumers decoupled and allows swapping implementations. A single implementation can satisfy multiple abstractions.
Tip
If a binding is missing, injection still works when the consumer requests a concrete type (not an abstraction).
Limitations: explicit bindings add configuration lines, but the trade-off is clearer architecture and safer evolution. Common pitfalls:
- Mixing abstraction-first and concrete-only styles in one module without clear boundaries.
- Forgetting to bind alternate implementations for tagged use cases. See also: Auto-bindings, Tags.
The following partial class will be generated:
partial class Composition
{
public VehicleComputer VehicleComputer
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return new VehicleComputer(new NavigationSystem(new GpsSensor()));
}
}
}Class diagram:
---
config:
maxTextSize: 2147483647
maxEdges: 2147483647
class:
hideEmptyMembersBox: true
---
classDiagram
GpsSensor --|> IGpsSensor
NavigationSystem --|> INavigationSystem
Composition ..> VehicleComputer : VehicleComputer VehicleComputer
NavigationSystem *-- GpsSensor : IGpsSensor
VehicleComputer *-- NavigationSystem : INavigationSystem
namespace Pure.DI.UsageTests.Basics.InjectionsOfAbstractionsScenario {
class Composition {
<<partial>>
+VehicleComputer VehicleComputer
}
class GpsSensor {
<<class>>
+GpsSensor()
}
class IGpsSensor {
<<interface>>
}
class INavigationSystem {
<<interface>>
}
class NavigationSystem {
<<class>>
+NavigationSystem(IGpsSensor sensor)
}
class VehicleComputer {
<<class>>
+VehicleComputer(INavigationSystem navigationSystem)
}
}