-
-
Notifications
You must be signed in to change notification settings - Fork 72
Description
Description
When Unity v5 introduced 'smart' constructor selection it was not thought through very well. It generally works in flat container scenarios, but in hierarchies behavior is somewhat lacking. There are three different scenarios of constructor selection:
- Constructor is specified by InjectionConstructor injection member
- Constructor is annotated with InjectionConstructor attribute
- Constructor is selected by Unity
Constructor is specified by InjectionConstructor
When constructor is specified by injection, it follows the same logic as the annotated case. The only difference, the registration creates a scope. It means that all dependencies will be resolved from the same container where this registration resides.
Constructor annotated with InjectionConstructor attribute
When one of the constructors of a type marked with InjectionConstructor attribute, the container generates a pipeline with that constructor. If type is registered explicitly, the registration determines the scope of that pipeline.
When resolving, container does not verify dependency availability and simply attempts to resolve all dependencies of the constructor. If the current scope does not have them available, the resolution fails.
Constructor is selected by Unity
This is where things become complicated. There are two algorithms available to select constructor:
Legacy Constructor Selector
Legacy algorithm is available via Unity.Extension.Legacy extension. This algorithm is compatible with 4.0.1 version of Unity and simply selects the longest accessible, non static, non private constructor with supported parameter types.
Smart Constructor Selector
This algorithm will check every accessible constructor and pick one with the longest parameter list the container can successfully resolve.
Unity v5 analyses these constructor and creates resolver based on current state of the container. Subsequent registrations do not affect the constructor, once selected, it remains the same.
Proposed Selection Steps
Injected Constructor
If InjectionConstructor is found, the constructor is selected by the injected member.
Injected constructor always overrides any other selection methods and can only be set by registering type with instance of InjectionConstructor provided to the registration.
If the selected constructor's dependencies could not be satisfied, resolution fails.
Annotated Constructor
This step is applicable to both, registered and unregistered types. If type is registered but no InjectionConstructor is injected, processor will look for InjectionConstructor attribute.
If found, the marked constructor will be used to instantiate the instance.
At runtime no checks are performed. If the selected constructor could not be satisfied by the resolving container, resolution fails.
Legacy Constructor selector
If legacy constructor selector is installed via Legacy extension, it will pick a constructor with the longest list of parameters.
If the selected constructor's dependencies could not be satisfied by the resolving container, resolution fails.
When the extension is installed smart selector never used.
Smart Constructor selector
If neither of above methods produces a valid constructor the container will attempt to find suitable constructor based on current registrations. The selection is performed at runtime and follows these steps:
-
Container creates a list of accessible constructors sorted by number of parameters. The constructors with unsupported parameters, such as Ref or Out are excluded.
-
The container checks each constructor one by one until it finds one it could satisfy with either overridden, injected, or resolved dependencies.
-
Type is instantiated using selected constructor and dependencies.