C#/Unity 中的构造函数注入?
我正在使用 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");
当我在总线上调用解析方法时,我如何确定名为“timmy”的人“儿子”被注入,并且在解析火车时如何确定该人“爸爸”和“乔”的名字解决了吗?
我在想也许可以使用命名实例?但我不知所措。任何帮助将不胜感激。
顺便说一句,我宁愿不创建 IPerson 界面。
I'm using C# with Microsoft's Unity framework. I'm not quite sure how to solve this problem. It probably has something to do with my lack of understanding DI with Unity.
My problem can be summed up using the following example code:
class Train(Person p) { ... }
class Bus(Person p) { ... }
class Person(string name) { ... }
Person dad = new Person("joe");
Person son = new Person("timmy");
When I call the resolve method on Bus how can I be sure that the Person 'son' with the name 'timmy' is injected and when resolving Train how can I be sure that Person 'dad' with then name 'joe' is resolved?
I'm thinking maybe use named instances? But I'm at a loss. Any help would be appreciated.
As an aside, I would rather not create an IPerson interface.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
除非您分别将“joe”和“timmy”注册为命名依赖项,否则您无法确定“timmy”是否已注入 Schoolbus。事实上,如果您尝试将同一类的两个实例注册为未命名依赖项,您将得到一个不明确的设置,并且您将根本无法解析
Person
。一般来说,如果您必须注册大量命名实例,那么您可能会以错误的方式进行 DI。 DI 的主要思想是解决域服务而不是域对象。
DI 的主要思想是提供一种机制,允许您将抽象类型(接口或抽象类)解析为具体类型。您的示例没有抽象类型,因此它并没有多大意义。
Unless you register respectively "joe" and "timmy" as named dependencies, you can't be sure that "timmy" is injected into Schoolbus. In fact, if you attempt to register two instances of the same class as unnamed dependencies, you will have an ambiguous setup, and you will not be able to resolve
Person
at all.In general, if you have to register a lot of named instances you are probably going about DI in the wrong way. The main idea of DI is to resolve Domain Services more than Domain Objects.
The primary idea of DI is to provide a mechanism that allows you to resolve abstract types (interfaces or abstract classes) into concrete types. Your example has no abstract types, so it doesn't really make a lot of sense.
解决这个问题的一种方法是使用带有命名注册的注入构造函数。
One way to solve this would be to use an injection constructor with a named registration.
马克·西曼说得对。我对你的困惑表示同情。当我学习使用自动依赖注入容器时,我自己也经历过。问题是有许多有效且合理的方法来设计和使用对象。然而,其中只有一些方法适用于自动依赖注入容器。
我的个人经历:早在我学习如何使用 Unity 或 Castle Windsor 容器等控制反转容器之前,我就已经了解了对象构造和控制反转的 OO 原理。我养成了这样编写代码的习惯:
在这个设计中,我的 Foo 类负责将帐户保存到数据库。它需要一个帐号来做到这一点,并需要一项服务来完成肮脏的工作。这有点类似于上面提供的具体类,其中每个对象在构造函数中都采用一些唯一的值。当您使用自己的代码实例化对象时,这种方法效果很好。您可以在正确的时间传入适当的值。
然而,当我了解自动依赖注入容器时,我发现我不再手动实例化 Foo。容器将为我实例化构造函数参数。这对于IService这样的服务来说是一个很大的便利。但对于整数和字符串等,它显然不太适用。在这些情况下,它将提供默认值(例如整数为零)。相反,我习惯于传递特定于上下文的值,例如帐号、姓名等...因此我必须调整我的编码和设计风格,如下所示:
看来两个 Foo 类都是有效的设计。但第二个可以与自动依赖注入一起使用,而第一个则不能。
Mark Seeman got it right. And I sympathize with your confusion. I went through it myself when I learned to use automatic dependency injection containers. The problem is that there are many valid and reasonable ways to design and use objects. Yet only some of those approaches work with automatic dependency injectorion containers.
My personal history: I learned OO principles of object construction and Inversion Of Control long before I learned how to use Inversion of Control containers like the Unity or Castle Windsor containers. I acquired the habit of writing code like this:
In this design, my Foo class is responsible for saving accounts to the database. It needs an account number to do that and a service to do the dirty work. This is somewhat similar to the concreted classes you provided above, where each object takes some unique values in the constructor. This works fine when you instantiate the objects with your own code. You can pass in the appropriate values at the right time.
However, when I learned about automatic dependency injection containers, I found that I was no longer instantiating Foo by hand. The container would instantiate the constructor arguments for me. This was a great convenience for the services like IService. But it obviously does not work so well for integers and strings and the like. In those cases, it would provide a default value (like zero for an integer). Instead, I had been accustomed to passing in context-specific values like account number, name, etc... So I had to adjust my style of coding and design to be like this:
It appears that both Foo classes are valid designs. But the second is useable with automatic dependency injection, and the first is not.