Wiki

Clone wiki

DIContainer / Home

Контейнер самостоятельно создает зарегистрированные классы и предоставить необходимые им зависимости. От нас требуется создать контейнер ,

 var container = new Container();
используя ,
 using SimpleDIContainer.UserLayer;
и сконфигурировать его.

Далее проиллюстрирую на примере некоторого тестового класса, куда контейнер будет внедрять зависимости, если это указано в конфигурации и класс зарегистрирован в контейнере.

 public class Test1:ITest1

 {

   public Test1(ITestInterface testInterface,ITest3 test3,List<ITest4> test41)*
   {
    //когда контейнер будет создавать объект, он внедрит все необходимые
    //зависимости в конструктор, при условии, что они были зарегистрированы 
   }
   private IList<ITest4> _tests{get;set;}// не сможем в этом место внедрить,тк приватное проперти
   public IList<ITest4> tests{get;set;}//при конфигурации после создания объекта контейнер внедрит в проперти 
   //нужное значение, если это прописать в конфигурации
   public void BindMethod(IList<ITest4> tests)
   {
    //можно внедрить агрументы в метод , после создания объекта метод будет
    //вызван с необходимыми аргументами , если это прописать в конфигурации
   }
 }

Биндинг

Мы можем зарегистрировать объект по его интерфейсу:

container.Bind<ITest4, Test4>(); 
Тогда во всех местах внедрения (конструктор, проперти, аргументы методов) надо указывать зависимость именно через этот интерфейс.

Мы можем зарегистрировать объект как конкретную реализацию:

container.BindInstance<Test1>();
Тогда в местах внедрений указываем тип конкретного класса.

Также на один интерфейс можно зарегистрировать несколько реализаций, ровно как и зарегистрировать их на один абстрактный класс или класс-родитель.

container.Bind<ITest4, Test4>(); container.Bind<ITest4, Test41>(); 
Тогда мы можем внедрить все реализации , указав в месте внедрения тип, имплементирующий IEnumerable<ITest4> (IEnumerable<T>).

Типы жизненных циклов

После регистрации типа, мы должны указать способ предоставления его нам, например вот так:

container.Bind<ITest4, Test4>().AsSingle();

AsSingle - по умолчанию будет применен , если мы не указали иного, на каждое обращение к ITest4 будет выдаваться один и тот же инстанс объекта. Инстанс будет создан при первом обращении. AsTransient - на каждое обращение создаем новый инстанс.

Ленивое/не ленивое создание

Конструкции выше не приведут к созданию объекта незамедлительно, тк по умолчанию объект создается только после обращения к нему(например при внедрении в другой объект). Чтобы создать сразу, нужно вызвать ActivateNonLazy вот так:

container.Bind<ITest4, Test4>().AsSingle().ActivateNonLazy(); 
В таком случае, объект будет создан сразу после вызова ActivateNonLazy.

Внедрения в проперти

Внедрение в проперти рекомендуется делать при наличии локального умолчания, также обязательные зависимости рекомендуется внедрять через конструктор. В контейнере сознательно отказались от кастомных атрибутов для методов и пропертей, чтобы не связывать пользовательский код с кодом контейнера. Какие именно проперти внедрять - мы задаем в месте создания и конфигурации контейнера

container.Bind<ITest1, Test1>().BindAllProperties().AsSingle().ActivateNonLazy(); 
В таком случае мы попытаемся после создания объекта заинжектить все публичные проперти,указав в конфигурации .BindAllProperties().
container.Bind<ITest1, Test1>().BindProperty("Test1").AsSingle().ActivateNonLazy(); 
В таком случае мы попытаемся после создания объекта заинжектить проперти с названием Test1 ,указав в конфигурации .BindProperty("Test1").

Внедрение в метод

Внедрение в метод может быть актуально например при использовании фабричного метода.

container.Bind<ITest1, Test1>().BindMethod("BindMethod").AsSingle().ActivateNonLazy();
В таком случае после создание объекта мы ищем метод BindMethod и вызываем его, передав необходимые аргументы(при условии что они были зарегистрированы в контейнере), ,указав в конфигурации .BindMethod("BindMethod").

Ручное внедрение

Иногда мы хотим внедрить реализацию, которую мы не зарегистрировали в контейнере. Это выполнимо с следующим синтаксисом

ручное внедрение в конструктор нового экземпляра TestClass:

container.Bind<ITest1, Test1>().SetToConstructor(new TestClass()) .AsSingle().ActivateNonLazy();
Также нужно учитывать, что если тот же тип уже есть в контейнере, то возникнет конфликт (ошибка при создании). Можно комбинировать и часть зависимостей передать через SetToConstructor, а часть взять из контейнера, в таком случае, в конструкторе первыми по очереди идут зависимости от SetToConstructor, затем от контейнера.

ручное внедрение в проперти нового экземпляра Test3:

container.Bind<ITest1, Test1>().SetToProperty("test",new Test3()).AsSingle().ActivateNonLazy();
ручное внедрение в метод test объекта Test3:
container.Bind<ITest1, Test1>().SetToMethod("test",new Test3()) .AsSingle().ActivateNonLazy();
ручное внедрение инстанса

container.SetInstance<ITest1>(Test1).AsSingle().ActivateNonLazy();

Фабрики

Контейнер поддерживает кастомные фабрики, в конструктор фабрики можно внедрить необходимые зависимости из контейнера, по аналогии с обычными зарегистрированными классами, сама фабрика должна наследоваться от BindFactory<T>. В T ConstructObject() мы описываем логику фабрики. Мы можем указать биндинг фабрики явно:

container.SetFactory<Factory2>().ActivateNonLazy();
либо просто внедрить объект фабрики как инстанс - разница только синтаксическая(мы будем пользовать SetFactory если мы хотим явно выделить внедрение именно фабрик , чтобы в коде бросалось в глаза)

Ручная очистка

Контейнер можно очистить вручную от всех зависимостей:

container.Dispose();

Updated