C#/Unity 中的构造函数注入?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2015308/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Constructor Injection in C#/Unity?
提问by JP Richardson
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.
我将 C# 与 Microsoft 的 Unity 框架一起使用。我不太确定如何解决这个问题。这可能与我缺乏对 Unity DI 的理解有关。
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?
当我在 Bus 上调用 resolve 方法时,我如何确定注入了名为 'timmy' 的 Person 'son' 以及在解析 Train 时如何确定名为 'joe' 的 Person 'dad' 被解析?
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.
顺便说一句,我宁愿不创建 IPerson 界面。
采纳答案by Daniel Auger
One way to solve this would be to use an injection constructor with a named registration.
解决此问题的一种方法是使用具有命名注册的注入构造函数。
// 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
回答by Mark Seemann
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.
除非您分别将“joe”和“timmy”注册为命名依赖项,否则您无法确定“timmy”是否已注入 Schoolbus。事实上,如果你试图将同一个类的两个实例注册为未命名的依赖项,你将有一个不明确的设置,你将根本无法解决Person
。
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 Servicesmore than Domain Objects.
一般来说,如果您必须注册大量命名实例,您可能会以错误的方式进行 DI。DI 的主要思想是解决领域服务而不是领域对象。
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.
DI 的主要思想是提供一种机制,允许您将抽象类型(接口或抽象类)解析为具体类型。你的例子没有抽象类型,所以它没有多大意义。
回答by David Allen
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:
我的个人经历:早在我学会如何使用控制反转容器(如 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();
}
}
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.
在这个设计中,我的 Foo 类负责将帐户保存到数据库中。它需要一个帐号来完成这项工作,并需要一项服务来完成这项肮脏的工作。这有点类似于您上面提供的具体类,其中每个对象在构造函数中都采用一些唯一的值。当您使用自己的代码实例化对象时,这可以正常工作。您可以在正确的时间传入适当的值。
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:
但是,当我了解自动依赖注入容器时,我发现我不再手动实例化 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);
}
}
It appears that both Foo classes are valid designs. But the second is useable with automatic dependency injection, and the first is not.
看起来这两个 Foo 类都是有效的设计。但是第二个可用于自动依赖注入,而第一个则不是。