Инъектор конструктора в C # / Unity? - PullRequest
15 голосов
/ 06 января 2010

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

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

class Train(Person p) { ... }

class Bus(Person p) { ... }

class Person(string name) { ... }

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

Когда я вызываю метод разрешения на Bus, как я могу быть уверен, что Person «сын» с именем «timmy» вводится, и при разрешении Train, как я могу быть уверен, что Person «папа» с именем «joe» решен?

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

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

Ответы [ 3 ]

34 голосов
/ 06 января 2010

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

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

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

17 голосов
/ 06 января 2010

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

// 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
14 голосов
/ 04 сентября 2010

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

Моя личная история: я изучил ОО принципы построения объектов и Inversion Of Control задолго до того, как научился использовать контейнеры Inversion of 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 являются допустимыми проектами. Но второй можно использовать с автоматическим внедрением зависимостей, а первый - нет.

...