Sunday, October 17, 2010

C# inverse of control

I've used my simple inverse of control class, but I needed a singleton for a configuration class.
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>(RegisterScope.Singleton);
Or if you need a new instance, use the Resolve method without parameters.

ISomeObject someObject = Container.Resolve<ISomeObject>();

You may use this code in your project if it fits your needs. Fell free to add a comment if you have questions.

No comments:

Post a Comment