As requested by Heslacher, this is a real implementation where I wanted to cache the UserSetting
s because they will not change over the life span of a request.
As requested by Heslacher, this is a real implementation where I wanted to cache the UserSetting
s because they will not change over the life span of a request.
Suggested lazy implementation by microsoft Deferring the Resolution of Objects
Suggested lazy implementation by microsoft Deferring the Resolution of Objects
public class LazyTypeFactory
{
//used to stop the same class being created twice
private readonly object _lock = new object();
private readonly Dictionary<Type, Type> _lazyTypes = new Dictionary<Type, Type>();
private readonly ModuleBuilder _moduleBuilder;
public LazyTypeFactory(string assembly, string module)
{
var an = new AssemblyName(assembly);
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
_moduleBuilder = assemblyBuilder.DefineDynamicModule(module);
}
public Type GetOrCreateLazyType<TLazyCreateOrGetLazyType<TLazy, TInterface>(string name = null)
where TLazy : ILazy<TInterface>, TInterface
where TInterface : class
{
Type result;
var @interface = typeof (TInterface);
var @container = typeof (IUnityContainer);
var @lazy = typeof (TLazy);
var @func = typeof (Func<IUnityContainer, TInterface>);
name = name ?? @lazy.Name + "Impl";
name = @lazy.Namespace + "." + name;
if ([email protected])
{
throw new Exception("Expected TInterface to be a type of interface");
}
var @container = typeof (IUnityContainer);
var @lazy = typeof (TLazy);
var @func = typeof (Func<IUnityContainer, TInterface>);
name = @lazy.Namespace + "." + (name ?? @lazy.Name + "Impl");
lock (_lock)
{
if (!_lazyTypes.TryGetValueContainsKey(@lazy,))
out {
result)) = _lazyTypes[@lazy];
}
else
{
var typeBuilder = _moduleBuilder.DefineType(name, TypeAttributes.Public | TypeAttributes.Class, null);
typeBuilder.AddInterfaceImplementation(@lazy);
//private field for container
var containerFieldc = typeBuilder.DefineField("_c", @container, FieldAttributes.Public);
//private field for func
var funcFieldf = typeBuilder.DefineField("_f", @func, FieldAttributes.Public);
//private field for func result
var valueFieldv = typeBuilder.DefineField("_v", @interface, FieldAttributes.Public);
//constructor with func
CreateConstructor(typeBuilder, @container, containerFieldc, @func, funcFieldf);
//private property to get func result
var propertyBuilder = CreateValueProperty(typeBuilder, @interface, valueFieldv, @container, containerFieldc, @func, funcFieldf);
//interface methods
//call prop then invok method
foreach (var iMethod in @interface.GetMethods())
{
var mb = CreateOverride(typeBuilder, propertyBuilder, iMethod);
typeBuilder.DefineMethodOverride(mb, iMethod);
}
//property methods
foreach (var iProperty in @interface.GetProperties())
{
var pb = typeBuilder.DefineProperty(iProperty.Name, PropertyAttributes.None, iProperty.PropertyType, Type.EmptyTypes);
if (iProperty.GetMethod != null)
{
var iMethod = iProperty.GetMethod;
var mb = CreateOverride(typeBuilder, propertyBuilder, iMethod);
pb.SetGetMethod(mb);
}
if (iProperty.SetMethod != null)
{
var iMethod = iProperty.SetMethod;
var mb = CreateOverride(typeBuilder, propertyBuilder, iMethod);
pb.SetSetMethod(mb);
}
}
_lazyTypes[@lazy] = result = typeBuilder.CreateType();
}
}
return result;
}
private static void CreateConstructor(TypeBuilder typeBuilder, Type @container, FieldBuilder containerFieldc, Type @func, FieldBuilder funcFieldf)
{
var constructor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { @container, @func });
constructor.DefineParameter(1, ParameterAttributes.None, "container");
constructor.DefineParameter(2, ParameterAttributes.None, "func");
var cIl = constructor.GetILGenerator();
cIl.Emit(OpCodes.Ldarg_0);
cIl.Emit(OpCodes.Ldarg_1);
cIl.Emit(OpCodes.Stfld, containerFieldc);
cIl.Emit(OpCodes.Ldarg_0);
cIl.Emit(OpCodes.Ldarg_2);
cIl.Emit(OpCodes.Stfld, funcFieldf);
cIl.Emit(OpCodes.Ret);
}
private static PropertyBuilder CreateValueProperty(TypeBuilder typeBuilder, Type @interface, FieldBuilder valueFieldv, Type @container, FieldBuilder containerFieldc, Type @func, FieldBuilder funcFieldf)
{
var propertyBuilder = typeBuilder.DefineProperty("Value", PropertyAttributes.None, @interface, Type.EmptyTypes);
var get = typeBuilder.DefineMethod("get_Value", MethodAttributes.Public | MethodAttributes.Virtual, @interface, new Type[0]);
var getIl = get.GetILGenerator();
var skip = getIl.DefineLabel();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, valueFieldv);
getIl.Emit(OpCodes.Ldnull);
getIl.Emit(OpCodes.Bne_Un, skip);
//call _f and set to _v
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, funcFieldf);
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, containerFieldc);
getIl.Emit(OpCodes.Call, @func.GetMethod("Invoke", new[] { @container }));
getIl.Emit(OpCodes.Stfld, valueFieldv);
getIl.MarkLabel(skip);
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, valueFieldv);
getIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(get);
return propertyBuilder;
}
private static MethodBuilder CreateOverride(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, MethodInfo iMethod)
{
var mb = typeBuilder.DefineMethod(iMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual,
iMethod.ReturnType, iMethod.GetParameters().Select(x => x.ParameterType).ToArray());
var mIL = mb.GetILGenerator();
mIL.Emit(OpCodes.Ldarg_0);
mIL.Emit(OpCodes.Call, propertyBuilder.GetMethod);
for (var i = 0; i < iMethod.GetParameters().Length; i++)
{
mIL.Emit(OpCodes.Ldarg, i + 1);
}
mIL.Emit(OpCodes.Call, iMethod);
mIL.Emit(OpCodes.Ret);
return mb;
}
}
var factory = new LazyTypeFactory("Assembly.Lazy", "Module.Lazy");
var type = factory.GetOrCreateLazyType<ILazyContractCreateOrGetLazyType<ILazyContract, IContract>();
IUnityContainer container = new UnityContainer();
var factory = new LazyTypeFactory("Assembly.Lazy", "Module.Lazy");
var type = factory.GetOrCreateLazyType<ILazyContractCreateOrGetLazyType<ILazyContract, IContract>();
container.RegisterType<IContract, ContractImplementation>();
var resolve = new Func<IUnityContainer, IContract>(c => c.Resolve<IContract>());
container.RegisterType(typeof(ILazyContract), type, new InjectionConstructor(typeof(IUnityContainer), resolve));
var lazy = container.Resolve<ILazyContract>();
lazy.SomeFunction();
EDIT:
Made edits as suggested by Heslacher
public class LazyTypeFactory
{
//used to stop the same class being created twice
private readonly object _lock = new object();
private readonly Dictionary<Type, Type> _lazyTypes = new Dictionary<Type, Type>();
private readonly ModuleBuilder _moduleBuilder;
public LazyTypeFactory(string assembly, string module)
{
var an = new AssemblyName(assembly);
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
_moduleBuilder = assemblyBuilder.DefineDynamicModule(module);
}
public Type GetOrCreateLazyType<TLazy, TInterface>(string name = null)
where TLazy : ILazy<TInterface>, TInterface
where TInterface : class
{
Type result;
var @interface = typeof (TInterface);
if ([email protected])
{
throw new Exception("Expected TInterface to be a type of interface");
}
var @container = typeof (IUnityContainer);
var @lazy = typeof (TLazy);
var @func = typeof (Func<IUnityContainer, TInterface>);
name = @lazy.Namespace + "." + (name ?? @lazy.Name + "Impl");
lock (_lock)
{
if (!_lazyTypes.TryGetValue(@lazy, out result))
{
var typeBuilder = _moduleBuilder.DefineType(name, TypeAttributes.Public | TypeAttributes.Class, null);
typeBuilder.AddInterfaceImplementation(@lazy);
//private field for container
var containerField = typeBuilder.DefineField("_c", @container, FieldAttributes.Public);
//private field for func
var funcField = typeBuilder.DefineField("_f", @func, FieldAttributes.Public);
//private field for func result
var valueField = typeBuilder.DefineField("_v", @interface, FieldAttributes.Public);
CreateConstructor(typeBuilder, @container, containerField, @func, funcField);
//private property to get func result
var propertyBuilder = CreateValueProperty(typeBuilder, @interface, valueField, @container, containerField, @func, funcField);
//interface methods
foreach (var iMethod in @interface.GetMethods())
{
var mb = CreateOverride(typeBuilder, propertyBuilder, iMethod);
typeBuilder.DefineMethodOverride(mb, iMethod);
}
//property methods
foreach (var iProperty in @interface.GetProperties())
{
var pb = typeBuilder.DefineProperty(iProperty.Name, PropertyAttributes.None, iProperty.PropertyType, Type.EmptyTypes);
if (iProperty.GetMethod != null)
{
var iMethod = iProperty.GetMethod;
var mb = CreateOverride(typeBuilder, propertyBuilder, iMethod);
pb.SetGetMethod(mb);
}
if (iProperty.SetMethod != null)
{
var iMethod = iProperty.SetMethod;
var mb = CreateOverride(typeBuilder, propertyBuilder, iMethod);
pb.SetSetMethod(mb);
}
}
_lazyTypes[@lazy] = result = typeBuilder.CreateType();
}
}
return result;
}
private static void CreateConstructor(TypeBuilder typeBuilder, Type @container, FieldBuilder containerField, Type @func, FieldBuilder funcField)
{
var constructor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { @container, @func });
constructor.DefineParameter(1, ParameterAttributes.None, "container");
constructor.DefineParameter(2, ParameterAttributes.None, "func");
var cIl = constructor.GetILGenerator();
cIl.Emit(OpCodes.Ldarg_0);
cIl.Emit(OpCodes.Ldarg_1);
cIl.Emit(OpCodes.Stfld, containerField);
cIl.Emit(OpCodes.Ldarg_0);
cIl.Emit(OpCodes.Ldarg_2);
cIl.Emit(OpCodes.Stfld, funcField);
cIl.Emit(OpCodes.Ret);
}
private static PropertyBuilder CreateValueProperty(TypeBuilder typeBuilder, Type @interface, FieldBuilder valueField, Type @container, FieldBuilder containerField, Type @func, FieldBuilder funcField)
{
var propertyBuilder = typeBuilder.DefineProperty("Value", PropertyAttributes.None, @interface, Type.EmptyTypes);
var get = typeBuilder.DefineMethod("get_Value", MethodAttributes.Public | MethodAttributes.Virtual, @interface, new Type[0]);
var getIl = get.GetILGenerator();
var skip = getIl.DefineLabel();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, valueField);
getIl.Emit(OpCodes.Ldnull);
getIl.Emit(OpCodes.Bne_Un, skip);
//call _f and set to _v
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, funcField);
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, containerField);
getIl.Emit(OpCodes.Call, @func.GetMethod("Invoke", new[] { @container }));
getIl.Emit(OpCodes.Stfld, valueField);
getIl.MarkLabel(skip);
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, valueField);
getIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(get);
return propertyBuilder;
}
private static MethodBuilder CreateOverride(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, MethodInfo iMethod)
{
var mb = typeBuilder.DefineMethod(iMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual,
iMethod.ReturnType, iMethod.GetParameters().Select(x => x.ParameterType).ToArray());
var mIL = mb.GetILGenerator();
mIL.Emit(OpCodes.Ldarg_0);
mIL.Emit(OpCodes.Call, propertyBuilder.GetMethod);
for (var i = 0; i < iMethod.GetParameters().Length; i++)
{
mIL.Emit(OpCodes.Ldarg, i + 1);
}
mIL.Emit(OpCodes.Call, iMethod);
mIL.Emit(OpCodes.Ret);
return mb;
}
}
var factory = new LazyTypeFactory("Assembly.Lazy", "Module.Lazy");
var type = factory.GetOrCreateLazyType<ILazyContract, IContract>();
IUnityContainer container = new UnityContainer();
var factory = new LazyTypeFactory("Assembly.Lazy", "Module.Lazy");
var type = factory.GetOrCreateLazyType<ILazyContract, IContract>();
container.RegisterType<IContract, ContractImplementation>();
var resolve = new Func<IUnityContainer, IContract>(c => c.Resolve<IContract>());
container.RegisterType(typeof(ILazyContract), type, new InjectionConstructor(typeof(IUnityContainer), resolve));
var lazy = container.Resolve<ILazyContract>();
lazy.SomeFunction();
EDIT:
Made edits as suggested by Heslacher
public class LazyTypeFactory
{
//used to stop the same class being created twice
private readonly object _lock = new object();
private readonly Dictionary<Type, Type> _lazyTypes = new Dictionary<Type, Type>();
private readonly ModuleBuilder _moduleBuilder;
public LazyTypeFactory(string assembly, string module)
{
var an = new AssemblyName(assembly);
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
_moduleBuilder = assemblyBuilder.DefineDynamicModule(module);
}
public Type CreateOrGetLazyType<TLazy, TInterface>(string name = null)
where TLazy : ILazy<TInterface>, TInterface
where TInterface : class
{
Type result;
var @interface = typeof (TInterface);
var @container = typeof (IUnityContainer);
var @lazy = typeof (TLazy);
var @func = typeof (Func<IUnityContainer, TInterface>);
name = name ?? @lazy.Name + "Impl";
name = @lazy.Namespace + "." + name;
if ([email protected])
{
throw new Exception("Expected TInterface to be a type of interface");
}
lock (_lock)
{
if (_lazyTypes.ContainsKey(@lazy))
{
result = _lazyTypes[@lazy];
}
else
{
var typeBuilder = _moduleBuilder.DefineType(name, TypeAttributes.Public | TypeAttributes.Class, null);
typeBuilder.AddInterfaceImplementation(@lazy);
//private field for container
var c = typeBuilder.DefineField("_c", @container, FieldAttributes.Public);
//private field for func
var f = typeBuilder.DefineField("_f", @func, FieldAttributes.Public);
//private field for func result
var v = typeBuilder.DefineField("_v", @interface, FieldAttributes.Public);
//constructor with func
CreateConstructor(typeBuilder, @container, c, @func, f);
//private property to get func result
var propertyBuilder = CreateValueProperty(typeBuilder, @interface, v, @container, c, @func, f);
//interface methods
//call prop then invok method
foreach (var iMethod in @interface.GetMethods())
{
var mb = CreateOverride(typeBuilder, propertyBuilder, iMethod);
typeBuilder.DefineMethodOverride(mb, iMethod);
}
foreach (var iProperty in @interface.GetProperties())
{
var pb = typeBuilder.DefineProperty(iProperty.Name, PropertyAttributes.None, iProperty.PropertyType, Type.EmptyTypes);
if (iProperty.GetMethod != null)
{
var iMethod = iProperty.GetMethod;
var mb = CreateOverride(typeBuilder, propertyBuilder, iMethod);
pb.SetGetMethod(mb);
}
if (iProperty.SetMethod != null)
{
var iMethod = iProperty.SetMethod;
var mb = CreateOverride(typeBuilder, propertyBuilder, iMethod);
pb.SetSetMethod(mb);
}
}
_lazyTypes[@lazy] = result = typeBuilder.CreateType();
}
}
return result;
}
private static void CreateConstructor(TypeBuilder typeBuilder, Type @container, FieldBuilder c, Type @func, FieldBuilder f)
{
var constructor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { @container, @func });
constructor.DefineParameter(1, ParameterAttributes.None, "container");
constructor.DefineParameter(2, ParameterAttributes.None, "func");
var cIl = constructor.GetILGenerator();
cIl.Emit(OpCodes.Ldarg_0);
cIl.Emit(OpCodes.Ldarg_1);
cIl.Emit(OpCodes.Stfld, c);
cIl.Emit(OpCodes.Ldarg_0);
cIl.Emit(OpCodes.Ldarg_2);
cIl.Emit(OpCodes.Stfld, f);
cIl.Emit(OpCodes.Ret);
}
private static PropertyBuilder CreateValueProperty(TypeBuilder typeBuilder, Type @interface, FieldBuilder v, Type @container, FieldBuilder c, Type @func, FieldBuilder f)
{
var propertyBuilder = typeBuilder.DefineProperty("Value", PropertyAttributes.None, @interface, Type.EmptyTypes);
var get = typeBuilder.DefineMethod("get_Value", MethodAttributes.Public | MethodAttributes.Virtual, @interface, new Type[0]);
var getIl = get.GetILGenerator();
var skip = getIl.DefineLabel();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, v);
getIl.Emit(OpCodes.Ldnull);
getIl.Emit(OpCodes.Bne_Un, skip);
//call _f and set to _v
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, f);
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, c);
getIl.Emit(OpCodes.Call, @func.GetMethod("Invoke", new[] { @container }));
getIl.Emit(OpCodes.Stfld, v);
getIl.MarkLabel(skip);
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, v);
getIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(get);
return propertyBuilder;
}
private static MethodBuilder CreateOverride(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, MethodInfo iMethod)
{
var mb = typeBuilder.DefineMethod(iMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual,
iMethod.ReturnType, iMethod.GetParameters().Select(x => x.ParameterType).ToArray());
var mIL = mb.GetILGenerator();
mIL.Emit(OpCodes.Ldarg_0);
mIL.Emit(OpCodes.Call, propertyBuilder.GetMethod);
for (var i = 0; i < iMethod.GetParameters().Length; i++)
{
mIL.Emit(OpCodes.Ldarg, i + 1);
}
mIL.Emit(OpCodes.Call, iMethod);
mIL.Emit(OpCodes.Ret);
return mb;
}
}
var factory = new LazyTypeFactory("Assembly.Lazy", "Module.Lazy");
var type = factory.CreateOrGetLazyType<ILazyContract, IContract>();
IUnityContainer container = new UnityContainer();
var factory = new LazyTypeFactory("Assembly.Lazy", "Module.Lazy");
var type = factory.CreateOrGetLazyType<ILazyContract, IContract>();
container.RegisterType<IContract, ContractImplementation>();
var resolve = new Func<IUnityContainer, IContract>(c => c.Resolve<IContract>());
container.RegisterType(typeof(ILazyContract), type, new InjectionConstructor(typeof(IUnityContainer), resolve));
var lazy = container.Resolve<ILazyContract>();
lazy.SomeFunction();