Reintroduce module container

This commit is contained in:
Mike Bierlee 2015-01-25 13:37:14 +01:00
parent a73b2b9b8b
commit 1d74064c15
4 changed files with 200 additions and 212 deletions

View file

@ -7,7 +7,7 @@
module poodinis.autowire; module poodinis.autowire;
public import poodinis.dependency; public import poodinis.container;
import std.typetuple; import std.typetuple;
@ -26,13 +26,13 @@ alias Autowired = Autowire;
public void autowire(Type)(DependencyContainer container, Type instance) { public void autowire(Type)(DependencyContainer container, Type instance) {
// For the love of god, refactor this! // For the love of god, refactor this!
debug(poodinisVerbose) { debug(poodinisVerbose) {
auto instanceType = typeid(Type); auto instanceType = typeid(Type);
auto instanceAddress = &instance; auto instanceAddress = &instance;
writeln(format("DEBUG: Autowiring members of [%s@%s]", instanceType, instanceAddress)); writeln(format("DEBUG: Autowiring members of [%s@%s]", instanceType, instanceAddress));
} }
foreach (member ; __traits(allMembers, Type)) { foreach (member ; __traits(allMembers, Type)) {
static if(__traits(compiles, __traits(getMember, Type, member)) && __traits(compiles, __traits(getAttributes, __traits(getMember, Type, member)))) { static if(__traits(compiles, __traits(getMember, Type, member)) && __traits(compiles, __traits(getAttributes, __traits(getMember, Type, member)))) {
foreach(autowireAttribute; __traits(getAttributes, __traits(getMember, Type, member))) { foreach(autowireAttribute; __traits(getAttributes, __traits(getMember, Type, member))) {
@ -40,11 +40,11 @@ public void autowire(Type)(DependencyContainer container, Type instance) {
if (__traits(getMember, instance, member) is null) { if (__traits(getMember, instance, member) is null) {
alias memberReference = TypeTuple!(__traits(getMember, instance, member)); alias memberReference = TypeTuple!(__traits(getMember, instance, member));
alias MemberType = typeof(memberReference)[0]; alias MemberType = typeof(memberReference)[0];
debug(poodinisVerbose) { debug(poodinisVerbose) {
string qualifiedInstanceTypeString = typeid(MemberType).toString; string qualifiedInstanceTypeString = typeid(MemberType).toString;
} }
MemberType qualifiedInstance; MemberType qualifiedInstance;
static if (is(autowireAttribute == Autowire!T, T) && !is(autowireAttribute.qualifier == UseMemberType)) { static if (is(autowireAttribute == Autowire!T, T) && !is(autowireAttribute.qualifier == UseMemberType)) {
alias QualifierType = typeof(autowireAttribute.qualifier); alias QualifierType = typeof(autowireAttribute.qualifier);
@ -55,15 +55,15 @@ public void autowire(Type)(DependencyContainer container, Type instance) {
} else { } else {
qualifiedInstance = container.resolve!(typeof(memberReference)); qualifiedInstance = container.resolve!(typeof(memberReference));
} }
__traits(getMember, instance, member) = qualifiedInstance; __traits(getMember, instance, member) = qualifiedInstance;
debug(poodinisVerbose) { debug(poodinisVerbose) {
auto qualifiedInstanceAddress = &qualifiedInstance; auto qualifiedInstanceAddress = &qualifiedInstance;
writeln(format("DEBUG: Autowired instance [%s@%s] to [%s@%s].%s", qualifiedInstanceTypeString, qualifiedInstanceAddress, instanceType, instanceAddress, member)); writeln(format("DEBUG: Autowired instance [%s@%s] to [%s@%s].%s", qualifiedInstanceTypeString, qualifiedInstanceAddress, instanceType, instanceAddress, member));
} }
} }
break; break;
} }
} }
@ -80,4 +80,4 @@ mixin template AutowireConstructor() {
public void globalAutowire(Type)(Type instance) { public void globalAutowire(Type)(Type instance) {
DependencyContainer.getInstance().autowire(instance); DependencyContainer.getInstance().autowire(instance);
} }

View file

@ -1,12 +1,136 @@
/** /**
* Poodinis Dependency Injection Framework * Poodinis Dependency Injection Framework
* Copyright 2014 Mike Bierlee * Copyright 2014 Mike Bierlee
* This software is licensed under the terms of the MIT license. * This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file. * The full terms of the license can be found in the LICENSE file.
*/ */
module poodinis.container; module poodinis.container;
pragma(msg, "Module \"container\" has been renamed to \"dependency\". The use of the \"container\" module is deprecated"); import std.string;
import std.array;
public import poodinis.dependency; import std.algorithm;
debug {
import std.stdio;
}
public import poodinis.registration;
public import poodinis.autowire;
class RegistrationException : Exception {
this(string message, TypeInfo registeredType, TypeInfo_Class concreteType) {
super(format("Exception while registering type %s to %s: %s", registeredType.toString(), concreteType.name, message));
}
}
class ResolveException : Exception {
this(string message, TypeInfo resolveType) {
super(format("Exception while resolving type %s: %s", resolveType.toString(), message));
}
}
deprecated("Container has been renamed to DependencyContainer")
alias Container = DependencyContainer;
class DependencyContainer {
private static DependencyContainer instance;
private Registration[][TypeInfo] registrations;
private Registration[] autowireStack;
public Registration register(ConcreteType)() {
return register!(ConcreteType, ConcreteType)();
}
public Registration register(InterfaceType, ConcreteType : InterfaceType)() {
TypeInfo registeredType = typeid(InterfaceType);
TypeInfo_Class concreteType = typeid(ConcreteType);
debug(poodinisVerbose) {
writeln(format("DEBUG: Register type %s (as %s)", concreteType.toString(), registeredType.toString()));
}
auto existingCandidates = registeredType in registrations;
if (existingCandidates) {
auto existingRegistration = getRegistration(*existingCandidates, concreteType);
if (existingRegistration) {
return existingRegistration;
}
}
Registration newRegistration = new Registration(registeredType, concreteType);
newRegistration.singleInstance();
registrations[registeredType] ~= newRegistration;
return newRegistration;
}
private Registration getRegistration(Registration[] candidates, TypeInfo concreteType) {
foreach(existingRegistration ; candidates) {
if (existingRegistration.instantiatableType == concreteType) {
return existingRegistration;
}
}
return null;
}
public RegistrationType resolve(RegistrationType)() {
return resolve!(RegistrationType, RegistrationType)();
}
public QualifierType resolve(RegistrationType, QualifierType : RegistrationType)() {
TypeInfo resolveType = typeid(RegistrationType);
TypeInfo qualifierType = typeid(QualifierType);
debug(poodinisVerbose) {
writeln("DEBUG: Resolving type " ~ resolveType.toString() ~ " with qualifier " ~ qualifierType.toString());
}
auto candidates = resolveType in registrations;
if (!candidates) {
throw new ResolveException("Type not registered.", resolveType);
}
Registration registration = getQualifiedRegistration(resolveType, qualifierType, *candidates);
QualifierType instance = cast(QualifierType) registration.getInstance();
if (!autowireStack.canFind(registration)) {
autowireStack ~= registration;
this.autowire!(QualifierType)(instance);
autowireStack.popBack();
}
return instance;
}
private Registration getQualifiedRegistration(TypeInfo resolveType, TypeInfo qualifierType, Registration[] candidates) {
if (resolveType == qualifierType) {
if (candidates.length > 1) {
string candidateList = candidates.toConcreteTypeListString();
throw new ResolveException("Multiple qualified candidates available: " ~ candidateList ~ ". Please use a qualifier.", resolveType);
}
return candidates[0];
}
return getRegistration(candidates, qualifierType);
}
public void clearAllRegistrations() {
registrations.destroy();
}
public void removeRegistration(RegistrationType)() {
registrations.remove(typeid(RegistrationType));
}
public static DependencyContainer getInstance() {
if (instance is null) {
instance = new DependencyContainer();
}
return instance;
}
}

View file

@ -1,136 +0,0 @@
/**
* Poodinis Dependency Injection Framework
* Copyright 2014 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
module poodinis.dependency;
import std.string;
import std.array;
import std.algorithm;
debug {
import std.stdio;
}
public import poodinis.registration;
public import poodinis.autowire;
class RegistrationException : Exception {
this(string message, TypeInfo registeredType, TypeInfo_Class concreteType) {
super(format("Exception while registering type %s to %s: %s", registeredType.toString(), concreteType.name, message));
}
}
class ResolveException : Exception {
this(string message, TypeInfo resolveType) {
super(format("Exception while resolving type %s: %s", resolveType.toString(), message));
}
}
deprecated("Container has been renamed to DependencyContainer")
alias Container = DependencyContainer;
class DependencyContainer {
private static DependencyContainer instance;
private Registration[][TypeInfo] registrations;
private Registration[] autowireStack;
public Registration register(ConcreteType)() {
return register!(ConcreteType, ConcreteType)();
}
public Registration register(InterfaceType, ConcreteType : InterfaceType)() {
TypeInfo registeredType = typeid(InterfaceType);
TypeInfo_Class concreteType = typeid(ConcreteType);
debug(poodinisVerbose) {
writeln(format("DEBUG: Register type %s (as %s)", concreteType.toString(), registeredType.toString()));
}
auto existingCandidates = registeredType in registrations;
if (existingCandidates) {
auto existingRegistration = getRegistration(*existingCandidates, concreteType);
if (existingRegistration) {
return existingRegistration;
}
}
Registration newRegistration = new Registration(registeredType, concreteType);
newRegistration.singleInstance();
registrations[registeredType] ~= newRegistration;
return newRegistration;
}
private Registration getRegistration(Registration[] candidates, TypeInfo concreteType) {
foreach(existingRegistration ; candidates) {
if (existingRegistration.instantiatableType == concreteType) {
return existingRegistration;
}
}
return null;
}
public RegistrationType resolve(RegistrationType)() {
return resolve!(RegistrationType, RegistrationType)();
}
public QualifierType resolve(RegistrationType, QualifierType : RegistrationType)() {
TypeInfo resolveType = typeid(RegistrationType);
TypeInfo qualifierType = typeid(QualifierType);
debug(poodinisVerbose) {
writeln("DEBUG: Resolving type " ~ resolveType.toString() ~ " with qualifier " ~ qualifierType.toString());
}
auto candidates = resolveType in registrations;
if (!candidates) {
throw new ResolveException("Type not registered.", resolveType);
}
Registration registration = getQualifiedRegistration(resolveType, qualifierType, *candidates);
QualifierType instance = cast(QualifierType) registration.getInstance();
if (!autowireStack.canFind(registration)) {
autowireStack ~= registration;
this.autowire!(QualifierType)(instance);
autowireStack.popBack();
}
return instance;
}
private Registration getQualifiedRegistration(TypeInfo resolveType, TypeInfo qualifierType, Registration[] candidates) {
if (resolveType == qualifierType) {
if (candidates.length > 1) {
string candidateList = candidates.toConcreteTypeListString();
throw new ResolveException("Multiple qualified candidates available: " ~ candidateList ~ ". Please use a qualifier.", resolveType);
}
return candidates[0];
}
return getRegistration(candidates, qualifierType);
}
public void clearAllRegistrations() {
registrations.destroy();
}
public void removeRegistration(RegistrationType)() {
registrations.remove(typeid(RegistrationType));
}
public static DependencyContainer getInstance() {
if (instance is null) {
instance = new DependencyContainer();
}
return instance;
}
}

View file

@ -5,98 +5,98 @@
* The full terms of the license can be found in the LICENSE file. * The full terms of the license can be found in the LICENSE file.
*/ */
import poodinis.dependency; import poodinis.container;
import std.exception; import std.exception;
version(unittest) { version(unittest) {
interface TestInterface { interface TestInterface {
} }
class TestClass : TestInterface { class TestClass : TestInterface {
} }
class UnrelatedClass{ class UnrelatedClass{
} }
class FailOnCreationClass { class FailOnCreationClass {
this() { this() {
throw new Exception("This class should not be instantiated"); throw new Exception("This class should not be instantiated");
} }
} }
class AutowiredClass { class AutowiredClass {
} }
class ComponentClass { class ComponentClass {
@Autowire @Autowire
public AutowiredClass autowiredClass; public AutowiredClass autowiredClass;
} }
class ComponentCat { class ComponentCat {
@Autowire @Autowire
public ComponentMouse mouse; public ComponentMouse mouse;
} }
class ComponentMouse { class ComponentMouse {
@Autowire @Autowire
public ComponentCat cat; public ComponentCat cat;
} }
class Eenie { class Eenie {
@Autowire @Autowire
public Meenie meenie; public Meenie meenie;
} }
class Meenie { class Meenie {
@Autowire @Autowire
public Moe moe; public Moe moe;
} }
class Moe { class Moe {
@Autowire @Autowire
public Eenie eenie; public Eenie eenie;
} }
class Ittie { class Ittie {
@Autowire @Autowire
public Bittie bittie; public Bittie bittie;
} }
class Bittie { class Bittie {
@Autowire @Autowire
public Banana banana; public Banana banana;
} }
class Banana { class Banana {
@Autowire @Autowire
public Bittie bittie; public Bittie bittie;
} }
interface SuperInterface { interface SuperInterface {
} }
class SuperImplementation : SuperInterface { class SuperImplementation : SuperInterface {
@Autowire @Autowire
public Banana banana; public Banana banana;
} }
interface Color { interface Color {
} }
class Blue : Color { class Blue : Color {
} }
class Red : Color { class Red : Color {
} }
// Test register concrete type // Test register concrete type
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
auto registration = container.register!(TestClass)(); auto registration = container.register!(TestClass)();
assert(registration.registeredType == typeid(TestClass), "Type of registered type not the same"); assert(registration.registeredType == typeid(TestClass), "Type of registered type not the same");
} }
// Test resolve registered type // Test resolve registered type
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -105,7 +105,7 @@ version(unittest) {
assert(actualInstance !is null, "Resolved type is null"); assert(actualInstance !is null, "Resolved type is null");
assert(cast(TestClass) actualInstance, "Resolved class is not the same type as expected"); assert(cast(TestClass) actualInstance, "Resolved class is not the same type as expected");
} }
// Test register interface // Test register interface
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -114,13 +114,13 @@ version(unittest) {
assert(actualInstance !is null, "Resolved type is null"); assert(actualInstance !is null, "Resolved type is null");
assert(cast(TestInterface) actualInstance, "Resolved class is not the same type as expected"); assert(cast(TestInterface) actualInstance, "Resolved class is not the same type as expected");
} }
// Test resolve non-registered type // Test resolve non-registered type
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
assertThrown!ResolveException(container.resolve!(TestClass)(), "Resolving non-registered type does not fail"); assertThrown!ResolveException(container.resolve!(TestClass)(), "Resolving non-registered type does not fail");
} }
// Test clear registrations // Test clear registrations
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -128,14 +128,14 @@ version(unittest) {
container.clearAllRegistrations(); container.clearAllRegistrations();
assertThrown!ResolveException(container.resolve!(TestClass)(), "Resolving cleared type does not fail"); assertThrown!ResolveException(container.resolve!(TestClass)(), "Resolving cleared type does not fail");
} }
// Test get singleton of container // Test get singleton of container
unittest { unittest {
auto instance1 = DependencyContainer.getInstance(); auto instance1 = DependencyContainer.getInstance();
auto instance2 = DependencyContainer.getInstance(); auto instance2 = DependencyContainer.getInstance();
assert(instance1 is instance2, "getInstance does not return the same instance"); assert(instance1 is instance2, "getInstance does not return the same instance");
} }
// Test resolve single instance for type // Test resolve single instance for type
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -144,7 +144,7 @@ version(unittest) {
auto instance2 = container.resolve!(TestClass); auto instance2 = container.resolve!(TestClass);
assert(instance1 is instance2, "Resolved instance from single instance scope is not the each time it is resolved"); assert(instance1 is instance2, "Resolved instance from single instance scope is not the each time it is resolved");
} }
// Test resolve new instance for type // Test resolve new instance for type
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -153,7 +153,7 @@ version(unittest) {
auto instance2 = container.resolve!(TestClass); auto instance2 = container.resolve!(TestClass);
assert(instance1 !is instance2, "Resolved instance from new instance scope is the same each time it is resolved"); assert(instance1 !is instance2, "Resolved instance from new instance scope is the same each time it is resolved");
} }
// Test resolve existing instance for type // Test resolve existing instance for type
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -162,7 +162,7 @@ version(unittest) {
auto actualInstance = container.resolve!(TestClass); auto actualInstance = container.resolve!(TestClass);
assert(expectedInstance is actualInstance, "Resolved instance from existing instance scope is not the same as the registered instance"); assert(expectedInstance is actualInstance, "Resolved instance from existing instance scope is not the same as the registered instance");
} }
// Test autowire resolved instances // Test autowire resolved instances
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -172,7 +172,7 @@ version(unittest) {
auto autowiredInstance = container.resolve!AutowiredClass; auto autowiredInstance = container.resolve!AutowiredClass;
assert(componentInstance.autowiredClass is autowiredInstance, "Member is not autowired upon resolving"); assert(componentInstance.autowiredClass is autowiredInstance, "Member is not autowired upon resolving");
} }
// Test circular autowiring // Test circular autowiring
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -182,7 +182,7 @@ version(unittest) {
auto cat = container.resolve!ComponentCat; auto cat = container.resolve!ComponentCat;
assert(mouse.cat is cat && cat.mouse is mouse && mouse !is cat, "Circular dependencies should be autowirable"); assert(mouse.cat is cat && cat.mouse is mouse && mouse !is cat, "Circular dependencies should be autowirable");
} }
// Test remove registration // Test remove registration
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -190,69 +190,69 @@ version(unittest) {
container.removeRegistration!TestClass; container.removeRegistration!TestClass;
assertThrown!ResolveException(container.resolve!TestClass); assertThrown!ResolveException(container.resolve!TestClass);
} }
// Test autowiring does not autowire member where instance is non-null // Test autowiring does not autowire member where instance is non-null
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
auto existingA = new AutowiredClass(); auto existingA = new AutowiredClass();
auto existingB = new ComponentClass(); auto existingB = new ComponentClass();
existingB.autowiredClass = existingA; existingB.autowiredClass = existingA;
container.register!AutowiredClass; container.register!AutowiredClass;
container.register!(ComponentClass).existingInstance(existingB); container.register!(ComponentClass).existingInstance(existingB);
auto resolvedA = container.resolve!AutowiredClass; auto resolvedA = container.resolve!AutowiredClass;
auto resolvedB = container.resolve!ComponentClass; auto resolvedB = container.resolve!ComponentClass;
assert(resolvedB.autowiredClass is existingA && resolvedA !is existingA, "Autowiring shouldn't rewire member when it is already wired to an instance"); assert(resolvedB.autowiredClass is existingA && resolvedA !is existingA, "Autowiring shouldn't rewire member when it is already wired to an instance");
} }
// Test autowiring circular dependency by third-degree // Test autowiring circular dependency by third-degree
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
container.register!Eenie; container.register!Eenie;
container.register!Meenie; container.register!Meenie;
container.register!Moe; container.register!Moe;
auto eenie = container.resolve!Eenie; auto eenie = container.resolve!Eenie;
assert(eenie.meenie.moe.eenie is eenie, "Autowiring third-degree circular dependency failed"); assert(eenie.meenie.moe.eenie is eenie, "Autowiring third-degree circular dependency failed");
} }
// Test autowiring deep circular dependencies // Test autowiring deep circular dependencies
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
container.register!Ittie; container.register!Ittie;
container.register!Bittie; container.register!Bittie;
container.register!Banana; container.register!Banana;
auto ittie = container.resolve!Ittie; auto ittie = container.resolve!Ittie;
assert(ittie.bittie is ittie.bittie.banana.bittie, "Autowiring deep dependencies failed."); assert(ittie.bittie is ittie.bittie.banana.bittie, "Autowiring deep dependencies failed.");
} }
// Test autowiring deep circular dependencies with newInstance scope does not autowire new instance second time // Test autowiring deep circular dependencies with newInstance scope does not autowire new instance second time
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
container.register!(Ittie).newInstance(); container.register!(Ittie).newInstance();
container.register!(Bittie).newInstance(); container.register!(Bittie).newInstance();
container.register!(Banana).newInstance(); container.register!(Banana).newInstance();
auto ittie = container.resolve!Ittie; auto ittie = container.resolve!Ittie;
assert(ittie.bittie.banana.bittie.banana is null, "Autowiring deep dependencies with newInstance scope autowired a reoccuring type."); assert(ittie.bittie.banana.bittie.banana is null, "Autowiring deep dependencies with newInstance scope autowired a reoccuring type.");
} }
// Test autowiring type registered by interface fails (BUG test case) // Test autowiring type registered by interface fails (BUG test case)
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
container.register!Banana; container.register!Banana;
container.register!(SuperInterface, SuperImplementation); container.register!(SuperInterface, SuperImplementation);
SuperImplementation superInstance = cast(SuperImplementation) container.resolve!SuperInterface; SuperImplementation superInstance = cast(SuperImplementation) container.resolve!SuperInterface;
assert(superInstance.banana is null, "Autowire instance which was resolved by interface type, which was not expected to be possible"); assert(superInstance.banana is null, "Autowire instance which was resolved by interface type, which was not expected to be possible");
} }
// Test reusing a container after clearing all registrations // Test reusing a container after clearing all registrations
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -266,14 +266,14 @@ version(unittest) {
} }
assert(false); assert(false);
} }
// Test register multiple concrete classess to same interface type // Test register multiple concrete classess to same interface type
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
container.register!(Color, Blue); container.register!(Color, Blue);
container.register!(Color, Red); container.register!(Color, Red);
} }
// Test removing all registrations for type with multiple registrations. // Test removing all registrations for type with multiple registrations.
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -281,16 +281,16 @@ version(unittest) {
container.register!(Color, Red); container.register!(Color, Red);
container.removeRegistration!Color; container.removeRegistration!Color;
} }
// Test registering same registration again // Test registering same registration again
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
auto firstRegistration = container.register!(Color, Blue); auto firstRegistration = container.register!(Color, Blue);
auto secondRegistration = container.register!(Color, Blue); auto secondRegistration = container.register!(Color, Blue);
assert(firstRegistration is secondRegistration, "First registration is not the same as the second of equal types"); assert(firstRegistration is secondRegistration, "First registration is not the same as the second of equal types");
} }
// Test resolve registration with multiple qualifiers // Test resolve registration with multiple qualifiers
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -303,7 +303,7 @@ version(unittest) {
} }
assert(false); assert(false);
} }
// Test resolve registration with multiple qualifiers using a qualifier // Test resolve registration with multiple qualifiers using a qualifier
unittest { unittest {
auto container = new DependencyContainer(); auto container = new DependencyContainer();
@ -311,9 +311,9 @@ version(unittest) {
container.register!(Color, Red); container.register!(Color, Red);
auto blueInstance = container.resolve!(Color, Blue); auto blueInstance = container.resolve!(Color, Blue);
auto redInstance = container.resolve!(Color, Red); auto redInstance = container.resolve!(Color, Red);
assert(blueInstance !is redInstance, "Resolving type with multiple, different registrations yielded the same instance"); assert(blueInstance !is redInstance, "Resolving type with multiple, different registrations yielded the same instance");
assert(blueInstance !is null, "Resolved blue instance to null"); assert(blueInstance !is null, "Resolved blue instance to null");
assert(redInstance !is null, "Resolved red instance to null"); assert(redInstance !is null, "Resolved red instance to null");
} }
} }