Here is a new version, just a little bit more complex, but capable of handling singletons.
public enum RegisterScope
{
Instance = 1,
Singleton
}
public static class Container
{
private static IDictionary<Type, Type> _types = null;
private static IDictionary<Type, object> _singletonInstances = new Dictionary<Type, object>();
public static void AutoRegister()
{
if (_types == null)
{
_types = new Dictionary<Type, Type>();
Assembly assembly = Assembly.GetExecutingAssembly();
foreach (Type contractType in assembly.GetTypes())
{
if (contractType.IsInterface)
{
Type implementationType = assembly.GetType(contractType.FullName.Replace("Interfaces.I", "Implementation."));
if (implementationType != null)
_types[contractType] = implementationType;
}
}
}
}
public static void Register<TContract, TImplementation>(RegisterScope registerScope)
{
Register<TContract, TImplementation>();
if (registerScope == RegisterScope.Singleton)
_singletonInstances.Add(typeof(TImplementation), null);
}
public static void Register<TContract, TImplementation>()
{
if (_types == null) AutoRegister();
_types[typeof(TContract)] = typeof(TImplementation);
}
public static T Resolve<T>(RegisterScope registerScope)
{
return (T)Resolve(typeof(T), registerScope);
}
public static T Resolve<T>()
{
return (T)Resolve(typeof(T));
}
public static object Resolve(Type contract, RegisterScope registerScope)
{
if (_types == null) AutoRegister();
if (registerScope == RegisterScope.Singleton)
{
Type implementation = _types[contract];
if (!_singletonInstances.Keys.Contains(implementation))
_singletonInstances.Add(implementation, null);
}
return Resolve(contract);
}
public static object Resolve(Type contract)
{
if (_types == null) AutoRegister();
Type implementation = _types[contract];
if (!_singletonInstances.Keys.Contains(implementation))
return Instanciate(implementation);
else
{
if (_singletonInstances[implementation] == null)
_singletonInstances[implementation] = Instanciate(implementation);
return _singletonInstances[implementation];
}
}
private static object Instanciate(Type type)
{
ConstructorInfo constructor = type.GetConstructors()[0];
ParameterInfo[] constructorParameters = constructor.GetParameters();
return constructorParameters.Length == 0
? Activator.CreateInstance(type)
: constructor.Invoke(constructorParameters.Select(parameterInfo => Resolve(parameterInfo.ParameterType)).ToArray());
}
}
Same as the simple container, the only restriction is that you have to use a naming convention for default implementations.
- Interface example: YourNamespace.Interfaces.ISomeObject
- Implementation example: YourNamespace.Implementation.SomeObject
You may use the auto registration and then override some interface registration (for mockups, Web or services for instance).
The code to use afterwards... again, a single line.
ISomeObject someObject = Container.Resolve<ISomeObject>
ISomeObject someObject = Container.Resolve<ISomeObject>();