Skip to content

Instantly share code, notes, and snippets.

@mlehmk
Last active August 29, 2015 14:02
Show Gist options
  • Select an option

  • Save mlehmk/6181dfd04641d9396866 to your computer and use it in GitHub Desktop.

Select an option

Save mlehmk/6181dfd04641d9396866 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace NotifyImpl
{
public class NotifyFactory
{
private Dictionary<Type, Func<object>> factories = new Dictionary<Type,Func<object>>();
private Dictionary<Assembly, ModuleBuilder> builders = new Dictionary<Assembly, ModuleBuilder>();
private ReaderWriterLockSlim guard = new ReaderWriterLockSlim();
public T CreateNew<T>() where T : class
{
Func<object> factory;
guard.EnterReadLock();
try
{
if (factories.TryGetValue(typeof(T), out factory))
{
return (T)factory();
}
}
finally
{
guard.ExitReadLock();
}
guard.EnterUpgradeableReadLock();
try
{
if (!factories.TryGetValue(typeof(T), out factory))
{
guard.EnterWriteLock();
try
{
Type impl = CreateImpl(typeof(T));
factory = () => Activator.CreateInstance(impl);
factories.Add(typeof(T), factory);
}
finally
{
guard.ExitWriteLock();
}
}
}
finally
{
guard.ExitUpgradeableReadLock();
}
return (T)factory();
}
private Type CreateImpl(Type type)
{
if (type.IsInterface)
{
if (!type.IsPublic)
{
throw new TypeAccessException("Interface not public");
}
ModuleBuilder module_builder;
if (!builders.TryGetValue(type.Assembly, out module_builder))
{
AssemblyName impl_name = new AssemblyName(string.Format("DynamicAssembly_{0}", type.Assembly.FullName));
AssemblyBuilder assembly_builder = AssemblyBuilder.DefineDynamicAssembly(impl_name, AssemblyBuilderAccess.Run);
module_builder = assembly_builder.DefineDynamicModule(impl_name.Name);
builders.Add(type.Assembly, module_builder);
}
TypeBuilder type_builder = module_builder.DefineType(string.Format("NotifyImpl_{0}_{1:N}", type.Name, type.GUID), TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit, typeof(NotifyPropertyBase), new Type[] { type });
MethodInfo onpropertychanged = typeof(NotifyPropertyBase).GetMethod("OnPropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(string) }, null);
foreach (var m in type.GetMembers())
{
if (m.MemberType == System.Reflection.MemberTypes.Property)
{
PropertyInfo p = (PropertyInfo)m;
if (!(p.CanRead && p.CanWrite))
{
throw new ArgumentException("Property has non-public setter");
}
Type pt = p.PropertyType;
MethodInfo equality = null;
bool boxing = false;
FieldBuilder field_builder = type_builder.DefineField(string.Format("<{0}>k__BackingField", p.Name), pt, FieldAttributes.Private);
MethodBuilder get_method = type_builder.DefineMethod(p.GetGetMethod().Name, MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual, CallingConventions.HasThis, pt, null);
ILGenerator get_method_il = get_method.GetILGenerator();
get_method_il.Emit(OpCodes.Ldarg_0);
get_method_il.Emit(OpCodes.Ldfld, field_builder);
get_method_il.Emit(OpCodes.Ret);
MethodBuilder set_method = type_builder.DefineMethod(p.GetSetMethod().Name, MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual, CallingConventions.HasThis, null, new Type[] { pt });
ILGenerator set_method_il = set_method.GetILGenerator();
equality = pt.GetMethod("op_Equality", BindingFlags.Public | BindingFlags.Static, null, new Type[] { pt, pt }, null);
if (pt.IsValueType && !pt.IsPrimitive)
{
if (equality == null)
{
equality = pt.GetMethod("Equals", BindingFlags.Public | BindingFlags.Static, null, new Type[] { pt, pt }, null);
}
if (equality == null)
{
equality = typeof(object).GetMethod("Equals", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(object), typeof(object) }, null);
boxing = true;
}
}
set_method_il.Emit(OpCodes.Ldarg_0);
set_method_il.Emit(OpCodes.Ldfld, field_builder);
if (boxing) set_method_il.Emit(OpCodes.Box, pt);
set_method_il.Emit(OpCodes.Ldarg_1);
if (boxing) set_method_il.Emit(OpCodes.Box, pt);
var endif_label = set_method_il.DefineLabel();
if (equality != null)
{
set_method_il.Emit(OpCodes.Call, equality);
set_method_il.Emit(OpCodes.Brtrue_S, endif_label);
}
else
{
set_method_il.Emit(OpCodes.Beq_S, endif_label);
}
set_method_il.Emit(OpCodes.Ldarg_0);
set_method_il.Emit(OpCodes.Ldarg_1);
set_method_il.Emit(OpCodes.Stfld, field_builder);
set_method_il.Emit(OpCodes.Ldarg_0);
set_method_il.Emit(OpCodes.Ldstr, p.Name);
set_method_il.Emit(OpCodes.Callvirt, onpropertychanged);
set_method_il.MarkLabel(endif_label);
set_method_il.Emit(OpCodes.Ret);
PropertyBuilder property_builder = type_builder.DefineProperty(p.Name, PropertyAttributes.HasDefault, pt, null);
property_builder.SetGetMethod(get_method);
property_builder.SetSetMethod(set_method);
}
else if (m.MemberType == MemberTypes.Method)
{
MethodInfo method = (MethodInfo)m;
if (!(method.IsSpecialName && (method.Name.StartsWith("set_") || method.Name.StartsWith("get_"))))
{
throw new ArgumentException("Interface has non-property member");
}
}
else
{
throw new ArgumentException("Interface has non-property member");
}
}
type_builder.DefineDefaultConstructor(MethodAttributes.Public);
Type impl_type = type_builder.CreateType();
return impl_type;
}
throw new ArgumentException("Type is not an interface");
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NotifyImpl
{
public class NotifyPropertyBase : INotifyPropertyChanged
{
protected virtual void OnPropertyChanged(string name)
{
var property_changed = PropertyChanged;
if (property_changed != null)
{
property_changed(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment