Инъекция конструктора в С#/Unity?

Я использую С# с картой Microsoft Unity. Я не совсем уверен, как решить эту проблему. Вероятно, это связано с моим недостатком понимания DI с Unity.

Моя проблема может быть суммирована с использованием следующего примера кода:

class Train(Person p) { ... }

class Bus(Person p) { ... }

class Person(string name) { ... }

Person dad = new Person("joe");
Person son = new Person("timmy");

Когда я вызываю метод разрешения на шине, как я могу быть уверенным, что персональный "сын" с именем "timmy" вводится и при разрешении Train, как я могу быть уверен, что Person 'dad' с именем name 'joe' разрешено?

Я думаю, может быть, использовать именованные экземпляры? Но я в недоумении. Любая помощь будет оценена.

В стороне, я бы предпочел не создавать интерфейс IPerson.

Ответы

Ответ 1

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

// Register timmy this way  
Person son = new Person("Timmy");  
container.RegisterInstance<Person>("son", son);  

// OR register timmy this way  
container.RegisterType<Person>("son", new InjectionConstructor("Timmy"));  

// Either way, register bus this way.  
container.RegisterType<Bus>(new InjectionConstructor(container.Resolve<Person>("son")));  

// Repeat for Joe / Train

Ответ 2

Если вы не зарегистрируете соответственно "joe" и "timmy" как именованные зависимости, вы не можете быть уверены, что "timmy" вводится в Schoolbus. На самом деле, если вы попытаетесь зарегистрировать два экземпляра того же класса, что и неназванные зависимости, у вас будет неоднозначная настройка, и вы не сможете вообще разрешить Person.

В общем случае, если вам нужно зарегистрировать много именованных экземпляров, вы, вероятно, ошибетесь. Основная идея DI заключается в разрешении Доменных служб больше Объектов домена.

Основная идея DI - предоставить механизм, позволяющий разрешать абстрактные типы (интерфейсы или абстрактные классы) в конкретные типы. В вашем примере нет абстрактных типов, поэтому на самом деле это не имеет особого смысла.

Ответ 3

Марк Симен понял это правильно. И я сочувствую твоей неразберихе. Я сам прошел через это, когда научился использовать контейнеры для инъекций с автоматической загрузкой. Проблема в том, что существует много правильных и разумных способов проектирования и использования объектов. Тем не менее, только некоторые из этих подходов работают с контейнерами с инжекцией с автоматической зависимостью.

Моя личная история: я изучил принципы построения объектов и Inversion Of Control задолго до того, как узнал, как использовать контейнеры Inversion Control, такие как контейнеры Unity или Castle Windsor. Я приобрел привычку писать код следующим образом:

public class Foo
{
   IService _service;
   int _accountNumber;

   public Foo(IService service, int accountNumber)
   {
      _service = service;
      _accountNumber = accountNumber;
   }
   public void SaveAccount()
   {
       _service.Save(_accountNumber);

   }
}
public class Program
{
     public static void Main()
     {
        Foo foo = new Foo(new Service(),1234);
        foo.Save();
     }
}

В этом проекте мой класс Foo отвечает за сохранение учетных записей в базе данных. Для этого требуется номер учетной записи, а также служба для выполнения грязной работы. Это несколько похоже на конкретные классы, которые вы указали выше, где каждый объект принимает некоторые уникальные значения в конструкторе. Это отлично работает при создании объектов с помощью собственного кода. Вы можете перейти в соответствующие значения в нужное время.

Однако, когда я узнал об автоматических контейнерах для инъекций, я обнаружил, что я больше не создавал Foo вручную. Контейнер будет создавать экземпляры конструктора для меня. Это было большим удобством для таких сервисов, как IService. Но это, очевидно, не так хорошо работает для целых чисел и строк и т.п. В этих случаях он будет предоставлять значение по умолчанию (например, нуль для целого числа). Вместо этого я привык к передаче в контексте конкретных значений, таких как номер счета, имя и т.д. Поэтому мне пришлось настроить мой стиль кодирования и дизайна таким образом:

public class Foo
{
   IService _service;
   public Foo(IService service)
   {
      _service = service;
   }
   public void SaveAccount(int accountNumber)
   {
       _service.Save(accountNumber);

   }
}
public class Program
{
     public static void Main()
     {
        Foo foo = new Foo(new Service());
        foo.Save(1234);
     }
}

Похоже, что оба класса Foo являются действительными проектами. Но второй можно использовать с автоматической инъекцией зависимостей, а первая - нет.