6

When you use an IoC container, like so:

var svc = IoC.Resolve<IShippingService>();

How does the IoC Container choose which implementation of IShippingService to instantiate?

Further, if I am calling the above to replace the equivalent code:

var svc = new ShippingService(new ProductLocator(), 
   new PricingService(), new InventoryService(), 
   new TrackingRepository(new ConfigProvider()), 
   new Logger(new EmailLogger(new ConfigProvider())));

I assume that ProductLocator, PricingService, etc. are called out in the constructor parameters as Interfaces, not concrete classes. How does the IoC container know which implementations of IProductLocator, IPricingService, etc. to instantiate?

Is the IoC Container smart enough to use the same ConfigProvider for both dependencies (if that is the requirement)?

  • 1
    what language is that? – BЈовић Mar 21 '16 at 17:47
  • 2
    @BЈовић Looks like C#, but I don't think it really matters. – CodesInChaos Mar 21 '16 at 17:48
  • 2
    some ramblings (hence, not an answer): IoC containers require the same information to be provided that you would typically provide with instantiation logic. The simplest injections (concrete dependency, no other parameters required) can often be fulfilled automatically. Everything else (mapping interfaces to concretions, providing value construction parameters such as string or int) have to be provided by configuration. The specific question of the ConfigProvider is usually handled by configuring a named instance; that way multiple objects can share a single instance of a dependency. – Marvin Mar 21 '16 at 17:48
4

Because you pass configure to it which tells it how to do it. Typically this happens during application startup.

In the simplest case you could have a lot of lines like:

iocConfig.Bind<IShippingService>().To<ShippingService>();

There are different ways to define such a configuration, such as conventions (e.g. a concrete class matching the name of the interface apart from the I prefix), attributes or config files, each with their own advantages and disadvantages.

When testing you could create the stubs manually, or use a different configuration.


Most containers also support a way for the target site to influence the dependency resolution, for example by adding an attribute. Personally I haven't needed that so far.


Reuse of instances is determined by scope. Typically you have one scope where nothing gets reused (transient), per-request scope, per-thread scope, singleton scope, etc. You typically specify the scope as part of the configuration.

3

Containers require setup. You need to tell it what you want when asking for an implementation of an interface.

If there's only one implementation, the container might be able to resolve it automatically, but even then, there are two cases:

1. The implementation depends only on concrete classes with no constructor parameters

If that's the case, the container usually looks up the dependencies, because it knows them and constructs them all, injecting it.

2. The implementation depends on either interfaces or classes which require types like strings, ints, etc.

In this case the parameters (be it the scalar types or concrete implementations) must once again be configured for the specific case.

If there are multiple implementations of the interface you're asking, you need to tell it what to do.


If you have an interface and want to choose an implementation during runtime, I don't think you can actually resolve it through an IoC container and a factory approach is generally recommended.

2

I'm going to answer this using a specific IoC, Castle Windsor to ground this into a concrete example. Different containers have slightly different functionality and naming conventions. Some of the details of your question are going to be implementation specific.

There are two basic ways to register a component one by one, and by convention. In one by one registration you list a component and the interface you want to register it for. You can also just indicate the type and have it registered to the interfaces it implements. Examples

container.Register(
   Component.For<IMyService>().ImplementedBy<MyServiceImpl>()
);
container.Register(
    Component.For<MyServiceImpl>()
);

By convention registration lets you specify some rules and it would register those types that match that. You can register types that implement a specific interface, or all types that live in an assembly.

To answer the implied question, of what happens when you have multiple implementations of an interface. Castle Windsor would return whichever is registered first. This may be a bit opaque but it is at least consistent.

When you register a type it is given a lifestyle, which describes when the component should be used. You have options that range from a new one every time(transient) to singleton. So if ConfigProvider was registered as transient it would be two different instances, if singleton it would be one instance. There are some other lifestyles available, the most common of those being per request.

2

I think it provides some further color to this topic by mentioning the mechanisms of Autofac as well.

Unlike CastleWindsor as mentioned by @Sign, Autofac returns whichever is registered last. I'm going to just quote Autofac docs as evidence:

If more than one component exposes the same service, Autofac will use the last registered component as the default provider of that service:

builder.Register<ConsoleLogger>().As<ILogger>();
builder.Register<FileLogger>().As<ILogger>();

In this scenario, FileLogger will be the default for ILogger because it was the last one registered.

The documentation above goes on to say that you can override that behavior by tacking on a final PreserveExistingDefaults() in the registration call.

But another interesting use case not mentioned in other answers here is this: what if you actually want all registered instances?

With Autofac this is simple--and has nothing to do with the registrations. It is how you use it. If you have a constructor that asks for an IEnumerable<> to be resolved, then it returns all registered instances. Using the above examples one would need only do this to get both ConsoleLogger and FileLogger:

public MyWorker(IEnumerable<ILogger> loggers) { ... }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.