Last active
August 29, 2015 14:02
-
-
Save mlehmk/6181dfd04641d9396866 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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"); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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