Edward Tanguay
Edward Tanguay

Reputation: 193302

Can someone explain the magic going on in Prism's resolve<> method?

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

Answers (2)

Glenn Block
Glenn Block

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

Daniel Earwicker
Daniel Earwicker

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

Related Questions