【问题标题】:Constructor Injection in C#/Unity?C#/Unity 中的构造函数注入?
【发布时间】:2011-01-02 04:27:59
【问题描述】:

我将 C# 与 Microsoft 的 Unity 框架一起使用。我不太确定如何解决这个问题。这可能与我对 Unity 的 DI 缺乏了解有关。

我的问题可以用下面的示例代码来总结:

class Train(Person p) { ... }

class Bus(Person p) { ... }

class Person(string name) { ... }

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

当我在 Bus 上调用 resolve 方法时,我如何确定名称为“timmy”的人“儿子”已被注入,当解析 Train 时,我如何确定名称为“joe”的人“爸爸”解决了吗?

我在想也许可以使用命名实例?但我不知所措。任何帮助将不胜感激。

顺便说一句,我宁愿不创建 IPerson 接口。

【问题讨论】:

    标签: c# dependency-injection unity-container constructor-injection


    【解决方案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 的主要思想是解决 Domain Services 而不是 Domain Objects

    DI 的主要思想是提供一种机制,允许您将抽象类型(接口或抽象类)解析为具体类型。您的示例没有抽象类型,所以它并没有多大意义。

    【讨论】:

    • 感谢您富有洞察力的回复。虽然您可能是对的,但 DI 和 IoC 对我来说是新概念,所以我仍在尝试找出最佳设计。
    • JP,一定要看看他的书。我发现它非常有见地。
    • 太棒了...听起来很有趣。我刚开始阅读介绍章节。
    • @MarkSeemann 如何注入共享资源,即两个控制器之间共享的ConcurrentQueue&lt;T&gt;?这也是依赖注入的有效候选者吗?
    • @Thomas 你还会如何分享它们?
    【解决方案3】:

    马克·西曼做对了。我同情你的困惑。当我学会使用自动依赖注入容器时,我自己经历过。问题是有许多有效和合理的方法来设计和使用对象。然而,只有其中一些方法适用于自动依赖注入容器。

    我的个人经历:早在我学会如何使用 Unity 或 Castle Windsor 容器等控制反转容器之前,我就学习了对象构造和控制反转的 OO 原则。我养成了这样写代码的习惯:

    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 类都是有效的设计。但是第二个可以用于自动依赖注入,第一个不行。

    【讨论】:

    • 第二个 Main() 方法不应该更像: Foo foo = new Foo(new Service()); foo.Save(1234); ?
    • 其实应该是 foo.SaveAccount(1234) :)
    • 这里的 int 初始化并不有趣。我只是有一个非常相似的案例(完全不同的域),我需要初始化超时——将其抽象为一个方法,
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多