static void ImplementPropertiesForInterface(Type intf, EmittedClass builder, List <PropertyInfo> props, EmittedField dirtyFlags, EmittedMethod propChanged) { var properties = intf.GetProperties(); foreach (var p in properties) { ImplementPropertyFor(intf, builder, props, p, dirtyFlags, propChanged); props.Add(p); } }
static void ImplementPropertyFor(Type intf, EmittedClass builder, List <PropertyInfo> props, PropertyInfo property, EmittedField dirtyFlags, EmittedMethod propChanged) { var fieldName = String.Concat("<", intf.Name, "_", property.Name, ">_field"); var prop = builder.DefinePropertyFromPropertyInfo(property); EmittedField field = null; var fieldType = property.PropertyType; Type observableCollection = null; Type genericArgType = null; if (fieldType.IsInterface && fieldType.IsGenericType) { var genericDef = fieldType.GetGenericTypeDefinition(); if (genericDef == typeof(IList <>) || genericDef == typeof(ICollection <>)) { genericArgType = fieldType.GetGenericArguments()[0]; observableCollection = typeof(ObservableCollection <>).MakeGenericType(genericArgType); field = builder.DefineField(fieldName, observableCollection); } } if (field == null) { field = builder.DefineField(fieldName, property.PropertyType); } prop.BindField(field); var captureDirtyFlagsIndex = props.Count; prop.AddGetter() .ContributeInstructions((m, il) => { il.LoadArg_0(); il.LoadField(field); }); if (property.CanWrite) { prop.AddSetter() .ContributeInstructions((m, il) => { var exit = il.DefineLabel(); il.DeclareLocal(typeof(bool)); il.Nop(); if (fieldType.IsArray) { var elmType = fieldType.GetElementType(); LoadFieldFromThisAndValue(il, field); il.Call(typeof(Extensions).GetMethod("EqualsOrItemsEqual", BindingFlags.Static | BindingFlags.Public) .MakeGenericMethod(elmType)); } else if (fieldType.IsClass) { var opEquality = fieldType.GetMethod("op_Equality", BindingFlags.Public | BindingFlags.Static); if (opEquality != null) { LoadFieldFromThisAndValue(il, field); il.Call(opEquality); } else { il.Call(typeof(EqualityComparer <>).MakeGenericType(fieldType) .GetMethod("get_Default", BindingFlags.Static | BindingFlags.Public)); LoadFieldFromThisAndValue(il, field); il.CallVirtual(typeof(IEqualityComparer <>).MakeGenericType(fieldType) .GetMethod("Equals", BindingFlags.Public | BindingFlags.Instance, null, new[] { fieldType, fieldType }, null )); } } else { LoadFieldFromThisAndValue(il, field); il.CompareEquality(fieldType); } il.StoreLocal_0(); il.LoadLocal_0(); il.BranchIfTrue_ShortForm(exit); il.Nop(); il.LoadArg_0(); il.LoadArg_1(); il.StoreField(field); il.LoadArg_0(); il.LoadFieldAddress(dirtyFlags); il.LoadValue(captureDirtyFlagsIndex); il.LoadValue(true); il.Call <BitVector>("set_Item"); il.Nop(); il.LoadArg_0(); il.LoadValue(prop.Name); il.Call(propChanged); il.Nop(); il.Nop(); il.MarkLabel(exit); }); } else if (observableCollection != null) { var observer = builder.DefineMethod(String.Concat("<", intf.Name, "_", property.Name, ">_field_CollectionChanged")); observer.ClearAttributes(); observer.IncludeAttributes(MethodAttributes.Private | MethodAttributes.HideBySig); observer.DefineParameter("sender", typeof(object)); observer.DefineParameter("e", typeof(NotifyCollectionChangedEventArgs)); observer.ContributeInstructions( (m, il) => { il.Nop(); il.LoadArg_0(); il.LoadFieldAddress(dirtyFlags); il.LoadValue(captureDirtyFlagsIndex); il.LoadValue(true); il.Call <BitVector>("set_Item"); il.Nop(); il.LoadArg_0(); il.LoadValue(property.Name); il.Call(propChanged); il.Nop(); }); var setter = prop.AddSetter(); setter.ExcludeAttributes(MethodAttributes.Public); setter.IncludeAttributes(MethodAttributes.Private); setter.ContributeInstructions((m, il) => { var isnull = il.DefineLabel(); var after = il.DefineLabel(); il.DeclareLocal <bool>(); il.Nop(); il.LoadArg_1(); il.LoadNull(); il.CompareEqual(); il.StoreLocal_0(); il.LoadLocal_0(); il.BranchIfTrue_ShortForm(isnull); il.Nop(); il.LoadArg_0(); il.LoadArg_1(); il.NewObj(observableCollection.GetConstructor(new[] { typeof(IEnumerable <>).MakeGenericType(genericArgType) })); il.StoreField(field); il.Nop(); il.Branch_ShortForm(after); il.MarkLabel(isnull); il.Nop(); il.LoadArg_0(); il.NewObj(observableCollection.GetConstructor(Type.EmptyTypes)); il.StoreField(field); il.Nop(); il.MarkLabel(after); il.LoadArg_0(); il.LoadField(field); il.LoadArg_0(); il.Emit(OpCodes.Ldftn, observer.Builder); // This seems really brittle... il.NewObj(typeof(NotifyCollectionChangedEventHandler).GetConstructor(new[] { typeof(object), typeof(IntPtr) })); il.CallVirtual(observableCollection.GetEvent("CollectionChanged") .GetAddMethod()); il.Nop(); }); } }