Reputation: 193302
I've got a CustomersModule.cs with the following Initialize() method:
public void Initialize()
{
container.RegisterType<ICustomersRepository, CustomersRepository>(new ContainerControlledLifetimeManager());
CustomersPresenter customersPresenter = this.container.Resolve<CustomersPresenter>();
}
The class I resolve from the container looks like this:
class CustomersPresenter
{
private CustomersView view;
private ICustomersRepository customersRespository;
public CustomersPresenter(CustomersView view,
ICustomersRepository customersRepository,
TestWhatever testWhatever)
{
this.view = view;
this.customersRespository = customersRepository;
}
}
The TestWhatever class is just a dummy class I created:
public class TestWhatever
{
public string Title { get; set; }
public TestWhatever()
{
Title = "this is the title";
}
}
Yet the container happily resolves CustomersPresenter
even though I never registered it, and also the container somehow finds TestWhatever, instantiates it, and injects it into CustomersPresenter
.
I was quite surprised to realize this since I couldn't find anywhere in the Prism documentation which explicitly stated that the container was so automatic.
So this is great, but it what else is the container doing that I don't know about i.e. what else can it do that I don't know about? For example, can I inject classes from other modules and if the modules happen to be loaded the container will inject them, and if not, it will inject a null?
Upvotes: 0
Views: 1070
Reputation: 8445
The reason this works is because Unity is designed for it. When you Resolve with a concrete type, Unity looks to see if it can resolve from the container. If it cannot, then it just goes and instantiates the type resolving it's dependencies. It's really quite simple.
Upvotes: 2
Reputation: 116674
There is nothing magical going on. You are specifying concrete types, so naturally they are resolvable, because if we have the Type
object, we can call a constructor on it.
class Fred { };
Fred f1 = new Fred();
Type t = typeof(Fred);
Fred f2 = (Fred)t.GetConstructor(Type.EmptyTypes).Invoke(null);
The last line above is effectively what happens, the type t having been found by using typeof
on the type parameter you give to Resolve
.
If the type cannot be constructed by new (because it's in some unknown separate codebase) then you wouldn't be able to give it as a type parameter to Resolve
.
In the second case, it is constructor injection, but it's still a known concrete constructable type. Via reflection, the Unity framework can get an array of all the Types of the parameters to the constructor. The type TestWhatever
is constructable, so there is no ambiguity or difficulty over what to construct.
As to your concern about separate modules (assemblies), if you move TestWhatever
to another assembly, that will not change the lines of code you've written; it will just mean that you have to add a reference to the other assembly to get this one to build. And then TestWhatever
is still an unambiguously refeferenced constructable type, so it can be constructed by Unity.
In other words, if you can refer to the type in code, you can get a Type
object, and so at runtime it will be directly constructable.
Response to comment:
If you delete the class TestWhatever
, you will get a compile-time error, because you refer to that type in your code. So it won't be possible to get a runtime by doing that.
The decoupling is still in effect in this arrangement, because you could register a specific instance of TestWhatever
, so every call to Resolve<TestWhatever>()
will get the same instance, rather than constructing a new one.
Upvotes: 3