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();
                    });
                }
            }