diff --git a/LightweightIocContainer/Registrations/DefaultRegistration.cs b/LightweightIocContainer/Registrations/DefaultRegistration.cs new file mode 100644 index 0000000..8b34300 --- /dev/null +++ b/LightweightIocContainer/Registrations/DefaultRegistration.cs @@ -0,0 +1,65 @@ +// Author: simon.gockner +// Created: 2019-05-20 +// Copyright(c) 2019 SimonG. All Rights Reserved. + +using System; +using LightweightIocContainer.Interfaces; +using LightweightIocContainer.Interfaces.Registrations; + +namespace LightweightIocContainer.Registrations +{ + /// + /// The default registration that is used to register a Type for the Interface it implements + /// + /// The registered Interface + public class DefaultRegistration : IDefaultRegistration + { + public DefaultRegistration(Type interfaceType, Type implementationType, Lifestyle lifestyle) + { + InterfaceType = interfaceType; + ImplementationType = implementationType; + Lifestyle = lifestyle; + + Name = $"{InterfaceType.Name}, {ImplementationType.Name}, Lifestyle: {Lifestyle.ToString()}"; + } + + /// + /// The name of the + /// + public string Name { get; } + + /// + /// The Type of the Interface that is registered with this + /// + public Type InterfaceType { get; } + + /// + /// The Type that implements the that is registered with this + /// + public Type ImplementationType { get; } + + /// + /// The Lifestyle of Instances that are created with this + /// + public Lifestyle Lifestyle { get; } + + + /// + /// This action is invoked when an instance of this type is created. + /// Can be set in the by calling + /// + public Action OnCreateAction { get; private set; } + + + /// + /// Pass an action that will be invoked when an instance of this type is created + /// + /// The action + /// The current instance of this + public IDefaultRegistration OnCreate(Action action) + { + OnCreateAction = action; + return this; + } + } +} \ No newline at end of file diff --git a/LightweightIocContainer/Registrations/RegistrationFactory.cs b/LightweightIocContainer/Registrations/RegistrationFactory.cs new file mode 100644 index 0000000..fd0b0b3 --- /dev/null +++ b/LightweightIocContainer/Registrations/RegistrationFactory.cs @@ -0,0 +1,38 @@ +// Author: simon.gockner +// Created: 2019-05-20 +// Copyright(c) 2019 SimonG. All Rights Reserved. + +using LightweightIocContainer.Interfaces; +using LightweightIocContainer.Interfaces.Registrations; + +namespace LightweightIocContainer.Registrations +{ + /// + /// A factory to register interfaces and factories in an and create the needed s + /// + public static class RegistrationFactory + { + /// + /// Register an Interface with a Type that implements it and create a + /// + /// The Interface to register + /// The Type that implements the + /// The for this + /// A new created with the given parameters + public static IDefaultRegistration Register(Lifestyle lifestyle = Lifestyle.Transient) where TImplementation : TInterface + { + return new DefaultRegistration(typeof(TInterface), typeof(TImplementation), lifestyle); + } + + /// + /// Register an Interface as an abstract typed factory and create a + /// + /// The abstract typed factory to register + /// The current + /// A new created with the given parameters + public static ITypedFactoryRegistration RegisterFactory(IInjectorContainer container) //TODO: Find a nicer way to inject the container into `TypedFactoryRegistration` + { + return new TypedFactoryRegistration(typeof(TFactory), container); + } + } +} \ No newline at end of file diff --git a/LightweightIocContainer/Registrations/TypedFactoryRegistration.cs b/LightweightIocContainer/Registrations/TypedFactoryRegistration.cs new file mode 100644 index 0000000..8a8097a --- /dev/null +++ b/LightweightIocContainer/Registrations/TypedFactoryRegistration.cs @@ -0,0 +1,128 @@ +// Author: simon.gockner +// Created: 2019-05-20 +// Copyright(c) 2019 SimonG. All Rights Reserved. + +using System; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using LightweightIocContainer.Exceptions; +using LightweightIocContainer.Factories; +using LightweightIocContainer.Interfaces; +using LightweightIocContainer.Interfaces.Factories; +using LightweightIocContainer.Interfaces.Registrations; + +namespace LightweightIocContainer.Registrations +{ + /// + /// The registration that is used to register an abstract typed factory + /// + /// The type of the abstract typed factory + public class TypedFactoryRegistration : ITypedFactoryRegistration + { + private readonly IInjectorContainer _container; + + public TypedFactoryRegistration(Type factoryType, IInjectorContainer container) + { + _container = container; + + InterfaceType = factoryType; + Name = $"{InterfaceType.Name}"; + + CreateFactory(); + } + + /// + /// The name of the + /// + public string Name { get; } + + /// + /// The Type of the abstract typed factory that is registered with this + /// + public Type InterfaceType { get; } + + /// + /// The class that contains the implemented abstract factory of this + /// + public ITypedFactory Factory { get; private set; } + + + /// + /// Creates the factory from the given abstract factory type + /// + /// Factory registration is invalid + private void CreateFactory() + { + var createMethods = InterfaceType.GetMethods().Where(m => m.ReturnType != typeof(void)).ToList(); + if (!createMethods.Any()) + throw new InvalidFactoryRegistrationException($"Factory {Name} has no create methods."); + + Type type = typeof(TypedFactory<>); + Type factory = type.MakeGenericType(InterfaceType); + + Factory = (ITypedFactory) Activator.CreateInstance(factory); + + AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Factory"), AssemblyBuilderAccess.Run); + ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("Factory"); + TypeBuilder typeBuilder = moduleBuilder.DefineType($"TypedFactory.{InterfaceType.Name}"); + + typeBuilder.AddInterfaceImplementation(InterfaceType); + + //add `private readonly IInjectorContainer _container` field + FieldBuilder containerFieldBuilder = typeBuilder.DefineField("_container", typeof(IInjectorContainer), FieldAttributes.Private | FieldAttributes.InitOnly); + + //add ctor + ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new[] {typeof(IInjectorContainer)}); + var constructorGenerator = constructorBuilder.GetILGenerator(); + constructorGenerator.Emit(OpCodes.Ldarg_0); + constructorGenerator.Emit(OpCodes.Ldarg_1); + constructorGenerator.Emit(OpCodes.Stfld, containerFieldBuilder); //set `_container` field + constructorGenerator.Emit(OpCodes.Ret); + + foreach (var createMethod in createMethods) + { + //create a method that looks like this + //public `createMethod.ReturnType` Create(`createMethod.GetParameters()`) + //{ + // return IInjectorContainer.Resolve(`createMethod.ReturnType`, params); + //} + + var args = createMethod.GetParameters(); + + MethodBuilder methodBuilder = typeBuilder.DefineMethod(createMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, + createMethod.ReturnType, (from arg in args select arg.ParameterType).ToArray()); + typeBuilder.DefineMethodOverride(methodBuilder, createMethod); + + var generator = methodBuilder.GetILGenerator(); + + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldfld, containerFieldBuilder); + generator.Emit(OpCodes.Ldtoken, createMethod.ReturnType); + + if (args.Any()) + { + generator.Emit(OpCodes.Ldc_I4_S, args.Length + 1); + generator.Emit(OpCodes.Newarr, typeof(object)); + + for (int i = 0; i < args.Length; i++) + { + generator.Emit(OpCodes.Dup); + generator.Emit(OpCodes.Ldc_I4_S, i); + generator.Emit(OpCodes.Ldarg_S, i + 1); + generator.Emit(OpCodes.Stelem_Ref); + } + } + else + { + generator.Emit(OpCodes.Ldc_I4_0); + } + + generator.EmitCall(OpCodes.Callvirt, typeof(IInjectorContainer).GetMethod(nameof(IInjectorContainer.Resolve), new[] { typeof(object), typeof(object)}), null); + generator.Emit(OpCodes.Ret); + } + + Factory.Factory = (TFactory) Activator.CreateInstance(typeBuilder.CreateTypeInfo().AsType(), _container); + } + } +} \ No newline at end of file