using System;
using System.Collections.Generic;
using System.Reflection;
using Pfz.Threading;
using Pfz.Extensions;
namespace Pfz.DynamicObjects
{
/// <summary>
/// Class for generating lazy-loaders.
/// </summary>
public sealed class LazyLoaderGenerator
{
private readonly LazyLoaderGenerator<byte> _generator = new LazyLoaderGenerator<byte>();
#region MustBeCollectible
/// <summary>
/// Identifies if the generated assemblies will be collectible or not.
/// </summary>
public bool MustBeCollectible
{
get
{
return _generator.MustBeCollectible;
}
set
{
_generator.MustBeCollectible = value;
}
}
#endregion
/// <summary>
/// Registers a lazy-loader function for a given T data-type.
/// Note that all properties of the same type will use the same loader
/// function, but they will receive the property name as their parameter,
/// so make sure the property-name is enough to get all the info you
/// need.
/// </summary>
public void RegisterLazyLoaderFor<T>(LazyType lazyType, LazyLoaderFunc<T> loaderFunc)
{
if (loaderFunc == null)
throw new ArgumentNullException("loaderFunc");
_generator.RegisterLazyLoaderFor<T>(lazyType, (ignored, name) => loaderFunc(name));
}
/// <summary>
/// Gets the creator for a given interface type.
/// </summary>
public Func<TInterface> GetCreator<TInterface>()
{
var result = _generator.GetCreator<TInterface>();
return () => result(0);
}
/// <summary>
/// Creates an instance of the given interface type built of lazy-loaders.
/// As no parameters are given, there's a big chance you will use it
/// like a singleton.
/// </summary>
public TInterface Create<TInterface>()
{
var creator = GetCreator<TInterface>();
return creator();
}
}
/// <summary>
/// Class for generating lazy-loaders that receive an user-instance.
/// </summary>
public sealed class LazyLoaderGenerator<TUserInstance>
{
private readonly Dictionary<Type, KeyValuePair<Delegate, LazyType>> _loaderFuncs = new Dictionary<Type, KeyValuePair<Delegate, LazyType>>();
#region MustBeCollectible
/// <summary>
/// Identifies if the generated assemblies will be collectible or not.
/// </summary>
public bool MustBeCollectible { get; set; }
#endregion
/// <summary>
/// Registers a lazy-loader function for a given T data-type.
/// Note that all properties of the same type will use the same loader
/// function, but they will receive the property name as their parameter,
/// so make sure the property-name is enough to get all the info you
/// need.
/// </summary>
public void RegisterLazyLoaderFor<T>(LazyType lazyType, LazyLoaderFunc<TUserInstance, T> loaderFunc)
{
if (loaderFunc == null)
throw new ArgumentNullException("loaderFunc");
var pair = new KeyValuePair<Delegate, LazyType>(loaderFunc, lazyType);
_loaderFuncs.Add(typeof(T), pair);
}
private static void _Add_Lazy_Property<T>(DelegatedTypeBuilder<TUserInstance> typeBuilder, string name, LazyLoaderFunc<TUserInstance, T> loaderFunc)
{
typeBuilder.AddPropertyWithField<T, Lazy<T>>
(
name,
(userInstance, field) => field.Value,
null,
(userInstance) => new Lazy<T>(() => loaderFunc(userInstance, name))
);
}
private static void _Add_LazyAndWeak_Property<T>(DelegatedTypeBuilder<TUserInstance> typeBuilder, string name, LazyLoaderFunc<TUserInstance, T> loaderFunc)
where
T: class
{
typeBuilder.AddPropertyWithField<T, LazyAndWeak<T>>
(
name,
(userInstance, field) => field.Value,
null,
(userInstance) => new LazyAndWeak<T>(() => loaderFunc(userInstance, name))
);
}
private static void _Add_BackgroundLoader_Property<T>(DelegatedTypeBuilder<TUserInstance> typeBuilder, string name, LazyLoaderFunc<TUserInstance, T> loaderFunc)
where
T: class
{
typeBuilder.AddPropertyWithField<T, BackgroundLoader<T>>
(
name,
(userInstance, field) => field.Value,
null,
(userInstance) => new BackgroundLoader<T>(() => loaderFunc(userInstance, name))
);
}
private static readonly MethodInfo _add_Lazy_PropertyMethod = typeof(LazyLoaderGenerator<TUserInstance>).GetMethod("_Add_Lazy_Property", BindingFlags.Static | BindingFlags.NonPublic);
private static readonly MethodInfo _add_LazyAndWeak_PropertyMethod = typeof(LazyLoaderGenerator<TUserInstance>).GetMethod("_Add_LazyAndWeak_Property", BindingFlags.Static | BindingFlags.NonPublic);
private static readonly MethodInfo _add_BackgroundLoader_PropertyMethod = typeof(LazyLoaderGenerator<TUserInstance>).GetMethod("_Add_BackgroundLoader_Property", BindingFlags.Static | BindingFlags.NonPublic);
private readonly Dictionary<Type, Delegate> _creators = new Dictionary<Type, Delegate>();
/// <summary>
/// Gets the creator for a given interface type.
/// </summary>
public Func<TUserInstance, TInterface> GetCreator<TInterface>()
{
if (!typeof(TInterface).IsInterface)
throw new ArgumentException("TInterface must be an interface type.");
Delegate untypedResult;
if (_creators.TryGetValue(typeof(TInterface), out untypedResult))
return (Func<TUserInstance, TInterface>)untypedResult;
var typeBuilder = new DelegatedTypeBuilder<TUserInstance>(null, MustBeCollectible, typeof(TInterface));
foreach(var property in typeof(TInterface).GetInterfaceProperties())
{
string name = property.Name;
var propertyType = property.PropertyType;
var loaderFuncPair = _loaderFuncs[propertyType];
var loaderFunc = loaderFuncPair.Key;
MethodInfo method;
switch(loaderFuncPair.Value)
{
case LazyType.LazyAndWeak:
method = _add_LazyAndWeak_PropertyMethod;
break;
case LazyType.Lazy:
method = _add_Lazy_PropertyMethod;
break;
case LazyType.BackgroundLoader:
method = _add_BackgroundLoader_PropertyMethod;
break;
default:
throw new InvalidOperationException("Invalid LazyType.");
}
method = method.MakeGenericMethod(propertyType);
var fastMethod = method.GetDelegate();
fastMethod(null, new object[]{typeBuilder, name, loaderFunc});
}
var creator = typeBuilder.CreateCreator();
Func<TUserInstance, TInterface> result =
(userInstance) =>
{
return (TInterface)creator(userInstance);
};
_creators.Add(typeof(TInterface), result);
return result;
}
}
}