Unity is added to the worker and unity is added to the worker.
Outline:
- Xiaoyin
- Shared sample code
- Use Lazy <T>
- Use automatic Factory
- Injection custom factory
Xiaoyin
When we say "parse a type/component", it usually means to call a type of constructor to create its instance ). However, in some cases, we want to avoid generating objects during parsing, but to create objects only when calling object methods are actually required. This method of parsing delayed object creation is called deferred resolution ).
Where is latency resolution usually used? A typical scenario is that the creation process of the object to be parsed takes a lot of time (for example, the multi-layer nest-like parsing may occur because the constructor needs to inject other objects ), we hope to speed up this process to speed up the response of applications.
This article introduces two methods to implement latency resolution: Lazy & lt; T & gt; and automatic factory ).
Shared sample code
To avoid repeating too many identical program codes in the future, we will first list the shared interfaces and classes.
Hypothetical situation
Assume that the application needs to provide a Message notification mechanism, which must support multiple sending channels, such as the email and SMS services (Short Message Service) push notification for mobile applications. For the sake of simplicity, only two services are implemented here, and the message sending part uses simple Console. WriteLine () to output the message, so as to conveniently observe the execution result of the program.
Design
A NotificationManager class is used as the administrator of the entire message notification function. Message notification mechanisms are provided by the following two types:
- EmailService: Send messages by email
- SmsService: Send messages through SMS
The above three classes all implement the same interface: IMessageService, and icationicationmanager only knows the IMessageService interface, instead of directly relying on the concrete class. Depicts their relationships:
Code
Message notification administrator code:
Public interface IMessageService {void SendMessage (string to, string msg);} public class EmailService: IMessageService {public void SendMessage (string to, string msg) {Console. writeLine ("send an email to {0} via EmailService }. ", To) ;}} public class SmsService: IMessageService {public void SendMessage (string to, string msg) {Console. WriteLine (" send SMS to {0} through SmsService }. ", );}}
The Constructor Injection method is used here, And the Constructor is passed into the message service by a Constructor. The Notify method uses the Message Service injected in advance to send messages to the specified recipient (independent variable ""). In the real world, you may need to use additional categories to represent the message receiving objects (for example, designing a MessageRecipient class to encapsulate the recipient's information ), this section is simplified to a considerable extent for demonstration purposes.
The program code for various types of Message Services is as follows:
Public interface IMessageService {void SendMessage (string to, string msg);} public class EmailService: IMessageService {public void SendMessage (string to, string msg) {Console. writeLine ("send an email to {0} via EmailService }. ", To) ;}} public class SmsService: IMessageService {public void SendMessage (string to, string msg) {Console. WriteLine (" send SMS to {0} through SmsService }. ", );}}
Use Lazy <T>
Unity allows us to directly use the built-in Lazy & lt; T & gt; of. NET Framework to implement latency resolution. The following two examples are compared:
// Var container = new UnityContainer (); container. registerType <IMessageService, EmailService> (); // register var svc = container. resolve <IMessageService> (); // resolution component (build function of call implementation class) svc. sendMessage ("Michael", "Hello! "); // Use components
Then there is the Lazy & lt; T & gt; latency parsing method. Because the registered program code remains unchanged, only the parsed part is listed:
Var lazyObj = container. resolve <Lazy <IMessageService> (); // delay parsing var svc = lazyObj. value; // The initiate svc of the call type. sendMessage ("Michael", "Hello! "); // Use components
Note: For Lazy & T & usage, refer to the MSDN online file or search for the keyword "Lazy of T 」.
Use automatic Factory
"Automatic factory" means that DI containers can automatically generate a lightweight factory category, so that we don't have to work on our own. In what situations will automatic factory be used? It is usually used to achieve "latency resolution", or to cope with more complex and dynamic requirements for late binding.
Take the scenario manager and IMessageService that have been prefixed as an example. Assuming that the EmailService component takes a long time (about five seconds) to create an instance, refer to the following program snippet:
Public class EmailService: IMessageService {public EmailService () {// it takes a long time to simulate the process of generating an object by suspending the thread. System. Threading. Thread. Sleep (5000);} // other program codes are not important here and are omitted .}
If the general component parsing method is used, it will be written as follows:
// (1) Register var container = new UnityContainer (); container. registerType <IMessageService, EmailService> (); // (2) Resolve var notySvc = container. resolve <icationicationmanager> (); // (3) Call notySvc. notify ("Michael", "Automated factory example ");
In this way, the "(2) Resolution" step will take the following actions:
That is to say, an EmailService object is created when icationicationmanager is parsed. Therefore, the process takes at least five seconds to complete. However, we want to delay the creation of dependent objects, that is, to create instances of dependent objects only when the method of calling a component is called. In such cases, we can use Func & lt; T & gt; and Unity's "Auto Factory" to achieve delayed resolution of dependent objects. The procedure is simple. You only need to modify the icationicationmanager category. As follows:
Class icationicationmanager {private IMessageService _ msgService; private Func <IMessageService> _ msgServiceFactory public NotificationManager (Func <IMessageService> svcFactory) {// Save the factory method in the delegate object _ msgServiceFactory = svcFactory;} public void every Y (string to, string msg) {// Since each call _ msgServiceFactory () creates a new IMessageService object. // here, a private member variable is used to save the previously created object, so as to avoid creating new instances. // Of course this is not necessary; in some cases, you may want to create new dependent objects each time. If (_ msgService = null) {_ msgService = _ msgServiceFactory ();} _ msgService. SendMessage (to, msg );}}
On the other hand, the original three steps of "registration, resolution, call" do not need to be changed. For easy reading, paste the program code of the registered component here:
// (1) Register var container = new UnityContainer (); container. RegisterType <IMessageService, EmailService> ();
Note that the icationicationmanager construction function requires the injection of Func & lt; IMessageService & gt;, but IMessageService is still written when registering components with the container, instead of Func & lt; IMessageService & gt; (five hairs for one ). In this way, when you want to add latency resolution (delayed dependency object creation) capabilities to the existing program code, you can modify less program code. This is the benefit of the Unity container auto factory.
To further explain, when the Unity container wants to parse icationicationmanager, it finds that the constructor requires a Func & lt; IMessageService & gt; delegate (delegate), so it will automatically generate this object for you, and inject it into the constructor of icationicationmanager class. Because the injection is a delegate object (you can regard it asFactory methodIn this case, the IMessageService object is not created. Instead, the IMessageService object is created by calling the Notify y method of the component in the upper-layer module.
Of course, the "Auto Factory" of the Unity container may not meet certain requirements. For example, the creation logic of some dependent objects is complicated and you need to write custom object factories. At this time, you may want to know how to inject custom factories.
Injection custom factory
When you want the Unity container to use your custom factory to create the dependent objects when parsing specific components, the InjectionFactory category of the Unity framework can be used.
Continue the icationicationmanager example in the previous section. Now suppose you have written an object factory to encapsulate the creation logic of IMessageService, as shown in the following code:
class MessageServiceFactory{ public IMessageService GetService() { bool isEmail = CheckIfEmailIsUsed(); if (isEmail) { return new EmailService(); } else { return new SmsService(); } }}
The GetService method of this object factory determines whether to return EmailService or SmsService instances based on certain variables in the execution period. Both EmailService and SmsService implement the IMessageService interface. Their program code is not important here, so it is not listed. To view program codes of these types, see the earlier section "shared sample programs.
The function builder of icationicationmanager is the same as the example in the previous section. It still injects Func & lt; IMessageService & gt ;. As follows:
Class icationicationmanager {private IMessageService _ msgService; private Func <IMessageService> _ msgServiceFactory public NotificationManager (Func <IMessageService> svcFactory) {// Save the factory method in the delegate object _ msgServiceFactory = svcFactory;} // (Other unimportant program code has been omitted )}
The rest of the work is to tell the Unity container: "When you need to parse IMessageService, please use my MessageServiceFactory to create an object .」 See the following program snippets:
Var container = new UnityContainer (); // register Func & lt; IMessageService & gt; factoryMethod = new MessageServiceFactory (). getService; container. registerType & lt; IMessageService & gt; (new InjectionFactory (c = & gt; factoryMethod (); // parse container. resolve & lt; icationicationmanager & gt ;();
The registration component section must be described as follows:
- Create a delegate object of Func & lt; IMessageService & gt; to point it to the GetService method of the MessageServiceFactory object.
- Then, call the RegisterType method of the Unity container and tell the container: when parsing IMessageService, use the GetService method of the custom factory provided by me, this factory method has already included the delegate object (variable factoryMethod) just created in the previous step, and wrapped the factory method another layer through Unity InjectionFactory to save it to the Unity container.
The RegisterType used in this example is an extension method. Its prototype is declared as follows:
public static IUnityContainer RegisterType<T>(this IUnityContainer container, params InjectionMember[] injectionMembers);
The InjectionFactory category inherits from InjectionMember, and the prototype of the constructor used in this example is declared:
public InjectionFactory(Func<IUnityContainer, object> factoryFunc);
Note: For more information about the InjectionFactory category, see online files.
This document is excerpted from:. NET dependency injection chapter 7th.