dependency_injection 4.5.0

dotnet add package dependency_injection --version 4.5.0
                    
NuGet\Install-Package dependency_injection -Version 4.5.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="dependency_injection" Version="4.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="dependency_injection" Version="4.5.0" />
                    
Directory.Packages.props
<PackageReference Include="dependency_injection" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add dependency_injection --version 4.5.0
                    
#r "nuget: dependency_injection, 4.5.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package dependency_injection@4.5.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=dependency_injection&version=4.5.0
                    
Install as a Cake Addin
#tool nuget:?package=dependency_injection&version=4.5.0
                    
Install as a Cake Tool

Overview

This library provides an implementation of a dependency injection container based on constructor injection. All instances are dealt with as std::shared_ptr<T>. Lifetime of each instance can be configured with an enum "lifetime" when registering types.
The most typical way to register a type "T" is to use a method register_type<T>(). This method registers a single constructor of the type "T" (The type "T" needs to have only one constructor to deduce an appropriate constructor unless a factory method or an instance is registered instead of the type constructor). Each constructor argument type "Arg" is automatically resolved and injected to the constructor as "std::shared_ptr<Arg>". This is done recursively until no argument resolution is required.
Constructor arguments can also be std::vector or std::list of std::shared_ptr<Arg>. In this case, all registrations associated with the type "Arg" are collected and injected to the constructor. One of constructor arguments can be "std::shared_ptr<container>". This allows you to inject the container itself to a class as a dependency. When lifetime of the class which owns the container is container_controlled or hierarchical, the method "unregister_all" in the container has to be called when destructing the container instance to avoid memory leak.
There are several different ways to register a type. For example, an interface type can be registered with its implementation type.

Features

  • Register a type with various ways
  • Resolve a registered type with automatic injection of its constructor arguments (Currently, the maximum number of arguments in a constructor is 20.)
  • Lifetime management using an enum dependency_injection::lifetime
  • Plug-in module is supported through container registration (Only windows now)

Example 1

Here is an example to register and resolve type dependencies.

struct A;
struct B;
struct C;
struct D;
struct E;
struct F;
struct G;

struct A
{
   std::shared_ptr<B> _b;
   std::vector<std::shared_ptr<C>> _cs;
   std::list<std::shared_ptr<D>> _ds;

   A(
      std::shared_ptr<B> b,
      std::vector<std::shared_ptr<C>> cs,
      std::list<std::shared_ptr<D>> ds) :
      _b(move(b)), _cs(move(cs)), _ds(move(ds))
   {
   }
};

struct B
{
   std::vector<std::shared_ptr<C>> _cs;
   std::shared_ptr<E> _e;
   std::shared_ptr<G> _g;

   B(
      std::vector<std::shared_ptr<C>> cs,
      std::shared_ptr<E> e,
      std::shared_ptr<G> g) :
      _cs(move(cs)), _e(move(e)), _g(move(g))
   {
   }
};

struct C
{
   std::shared_ptr<F> _f;
   C(std::shared_ptr<F> f) : _f(std::move(f)) {}
};

struct D
{
   std::shared_ptr<E> _e;
   D(std::shared_ptr<E> e) : _e(std::move(e)) {}
};

struct E
{
   std::shared_ptr<container> _container;
   E(std::shared_ptr<container> container) : _container(move(container)) {}
};

struct F
{
   std::shared_ptr<int> _value;
   F(std::shared_ptr<int> value) : _value(move(value)) {}
};

struct G
{
   double _value;
   G(double value) : _value(value) {}
};

struct GFactory
{
   std::shared_ptr<int> _value;
   GFactory(std::shared_ptr<int> value) : _value(value) {}

   std::shared_ptr<G> create() 
   {
      // Specific logic to instantiate G
      return std::make_shared<G>(*_value * 10.0);
   }
};

using namespace dependency_injection;
auto container = container::create();

container->register_type<A>(lifetime::per_resolve);
container->register_type<B>(lifetime::per_resolve);
container->register_type<C>(lifetime::per_resolve, "C1");
container->register_type<C>(lifetime::per_resolve, "C2");
container->register_type<D>(lifetime::per_resolve, "D1");
container->register_type<D>(lifetime::per_resolve, "D2");
container->register_type<E>(lifetime::per_resolve);
container->register_type<F>(lifetime::per_resolve);
container->register_type_with_factory<G, GFactory>(lifetime::per_resolve);
container->register_instance<int>(1);

auto a = container->resolve<A>();

Note that the container type cannot be registered but can be injected to a constructor of a type as shown in the type E in the example. Also, the type G is registered with a factory type GFactory to instruct specific logic for instantiation.

Example 2

Here is another example with a local module and a plug-in module. Code below defines sample classes and a local module "sample_module".

#include <module.h>
using namespace dependency_injection;

struct IChild
{
   virtual ~IChild() = default;
};

struct Parent
{
   std::vector<std::shared_ptr<IChild>> _children;
   Parent(std::vector<std::shared_ptr<IChild>> children) : _children(move(children)) {}
};

struct Child1 : public IChild {};
struct Child2 : public IChild {};

class sample_module : public i_module
{
public:
   virtual void on_register(const std::shared_ptr<container>& container) override
   {
      container->register_type<Parent>(lifetime::per_resolve);
      container->register_interface<IChild, Child1>(lifetime::per_resolve, "Child1");
      container->register_interface<IChild, Child2>(lifetime::per_resolve, "Child2");
   }

   virtual void on_initialize(const std::shared_ptr<container>& container) override {}
   virtual void on_close(const std::shared_ptr<container>& container) override {}
};

The local module "sample_module" can be extended by a plug-in module like below. A plug-in module is implemented in an external dll and can be exposed by implementing "plugin_module_api.h". Here is an example of a plug-in module as "sample_plugin_module" which is implemented in an external dll.

#include <module.h>
using namespace dependency_injection;

struct Child3 : public IChild {};
struct Child4 : public IChild {};

struct sample_plugin_module : public i_module
{
   virtual void on_register(const std::shared_ptr<container>& container) override
   {
      container->register_interface<IChild, Child3>(lifetime::per_resolve, "Child3");
      container->register_interface<IChild, Child4>(lifetime::per_resolve, "Child4");
   }

   virtual void on_initialize(const std::shared_ptr<container>& container) override {}
   virtual void on_close(const std::shared_ptr<container>& container) override {}
};

#include <plugin_module_api.h>
using namespace dependency_injection;

i_module* create_module()
{
   return new sample_plugin_module();
}

void delete_module(i_module* module)
{
   delete module;
}

A sample client application below consumes both the local module "sample_module" and the plug-in module "sample_plugin_module".

#include <module_manager.h>
#include <plugin_module.h>
#include <sample_classes.h>
#include <sample_module.h>
using namespace dependency_injection;

int main()
{
   auto module1 = std::make_shared<sample_module>();
   auto module2 = plugin_module::load(L"sample_plugin_module.dll");
   auto module_manager = module_manager::create({ module1, module2 });

   module_manager->register_modules();
   module_manager->initialize();

   auto container = module_manager->get_container();
   auto parent = container->resolve<Parent>();

   module_manager->close();
   return 0;
 }

The resultant instance of type "Parent" should contain all the children defined in both sample_module and sample_plugin_module. Dependency inversion over multiple modules is another good use case of the plug-in system.

Product Compatible and additional computed target framework versions.
native native is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.5.0 172 3/31/2024
4.4.2 124 3/24/2024
4.4.1 125 3/24/2024
4.3.0 380 1/8/2023
4.2.0 341 12/31/2022
4.1.1 345 12/30/2022
4.1.0 331 12/30/2022
4.0.0 335 12/30/2022
3.2.0 596 10/4/2020
3.1.2 492 10/3/2020
3.1.1 572 9/27/2020
3.0.0 550 5/12/2020
3.0.0-pre002 456 5/8/2020
2.1.0 533 4/26/2020
2.0.0 514 4/19/2020
2.0.0-pre004 644 2/6/2020
1.0.2 594 10/27/2019
1.0.1 1,157 9/23/2019
1.0.0 777 9/17/2019

Constructor registration is fully automated.