Implement INotifyPropertyChanged with dynamic code generation

Code for this post can be found here.

Andrea Saltarello, in his post of some days ago, told about POCO object and dynamic implementation of INotifyPropertyChanged.

The problem is, how to implement INotifyPropertyChanged with dynamic code generation? The answer is that is quite simple even if we do not relay on Castle.DynamicProxy, here is a simple domain class.

public class Customer { public virtual String Property { get { return property; } set { property = value; } } private String property; public virtual Int32 AnotherProp { get { return anotherProp; } set { anotherProp = value; } } private Int32 anotherProp; }

Now let’s see how you can create a dynamic type that inherits from it, and overrides all virtual properties implementing the INotifyPropertyChanged interface. The first step is to define the event PropertyChanged, so you need three methods, one for the add an event listener, the other for removing the event listener and the final one to raise the event. First step, create all the method info I’ll need for dynamic generation

MethodInfo DelegateCombine = typeof(Delegate).GetMethod("Combine", new Type[] { typeof(Delegate), typeof(Delegate) }); MethodInfo DelegateRemove = typeof(Delegate).GetMethod("Remove", new Type[] { typeof(Delegate), typeof(Delegate) }); MethodInfo InvokeDelegate = typeof (PropertyChangedEventHandler).GetMethod("Invoke"); FieldBuilder eventBack = mTypeBuilder.DefineField("PropertyChanged", typeof(PropertyChangingEventHandler), FieldAttributes.Private); ConstructorInfo CreateEventArgs = typeof (PropertyChangingEventArgs).GetConstructor(new Type[] {typeof (String)});

I need Delegate.Combine and Delegate.Remove, then I need the invoke method of the PropertyChangedEventHandler, then I need to create a field named PropertyChanged used to store the delegate and finally I need the constructorInfo for the PropertyChangingEventArgs. Now, armed with these, I begin to create the event.

1 MethodBuilder AddPropertyChanged = mTypeBuilder.DefineMethod( 2 "add_PropertyChanged", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.SpecialName | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot, 3 typeof(void), new Type[] { typeof(PropertyChangedEventHandler) }); 4 ILGenerator gen = AddPropertyChanged.GetILGenerator(); 5 gen.Emit(OpCodes.Ldarg_0); 6 gen.Emit(OpCodes.Ldarg_0); 7 gen.Emit(OpCodes.Ldfld, eventBack); 8 gen.Emit(OpCodes.Ldarg_1); 9 gen.Emit(OpCodes.Call, DelegateCombine); 10 gen.Emit(OpCodes.Castclass, typeof(PropertyChangedEventHandler)); 11 gen.Emit(OpCodes.Stfld, eventBack); 12 gen.Emit(OpCodes.Ret);

This is the code called when an object register a delegate to listen to the event. The IL code is really simple, in line 5 ad 6 load two times the instance of the object on the stack, with line 7 I push on the stack the content of the field storing the actual delegate, then in line 8 I push on the stack the new handler, and then call DelegateCombine. The result was cast to the right type and finally in line 11 I can store the new delegate in the field.

The remove part of the event is similar, it does not worth explanation. The third and final part of the event is the method called to raise the event itself.

1 MethodBuilder RaisePropertyChanged = mTypeBuilder.DefineMethod( 2 "OnPropertyChanged", MethodAttributes.Public, 3 typeof(void), new Type[] { typeof(String) }); 4 gen = RaisePropertyChanged.GetILGenerator(); 5 Label lblDelegateOk = gen.DefineLabel(); 6 gen.DeclareLocal(typeof(PropertyChangedEventHandler)); 7 gen.Emit(OpCodes.Nop); 8 gen.Emit(OpCodes.Ldarg_0); 9 gen.Emit(OpCodes.Ldfld, eventBack); 10 gen.Emit(OpCodes.Stloc_0); 11 gen.Emit(OpCodes.Ldloc_0); 12 gen.Emit(OpCodes.Ldnull); 13 gen.Emit(OpCodes.Ceq); 14 gen.Emit(OpCodes.Brtrue, lblDelegateOk); 15 gen.Emit(OpCodes.Ldloc_0); 16 gen.Emit(OpCodes.Ldarg_0); 17 gen.Emit(OpCodes.Ldarg_1); 18 gen.Emit(OpCodes.Newobj, CreateEventArgs); 19 gen.Emit(OpCodes.Callvirt, InvokeDelegate); 20 gen.MarkLabel(lblDelegateOk); 21 gen.Emit(OpCodes.Ret);

This method is a little more complex, but essentially it store the value of the actual delegate in a local variable, then compare it to null to check if someone is interested in the event. If the event handler is not null it simply invoke it, actually raising the event. Now the work is half done, we need to subclass each virtual property, calling this method to raise the event.

foreach (PropertyInfo pinfo in parent.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (pinfo.GetSetMethod().IsVirtual) { PropertyBuilder pb = mTypeBuilder.DefineProperty( pinfo.Name, PropertyAttributes.None, pinfo.PropertyType, Type.EmptyTypes); MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual; MethodBuilder getMethod = mTypeBuilder.DefineMethod( "get_" + pinfo.Name, getSetAttr, pinfo.PropertyType, Type.EmptyTypes); ILGenerator gen = getMethod.GetILGenerator(); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Call, pinfo.GetGetMethod()); gen.Emit(OpCodes.Ret); pb.SetGetMethod(getMethod); MethodBuilder setMethod = mTypeBuilder.DefineMethod( "set_" + pinfo.Name, getSetAttr, null, new Type[] { pinfo.PropertyType }); gen = setMethod.GetILGenerator(); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldstr, pinfo.Name); gen.Emit(OpCodes.Call, raiseEvent); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldarg_1); gen.Emit(OpCodes.Call, pinfo.GetSetMethod()); gen.Emit(OpCodes.Ret); pb.SetSetMethod(setMethod); } }

This method cycles through all properties, check if the property is virtual, then declare a corresponding property with MethodAttributes.Virtual, actually overriding each virtual property. The get part is really simple, I call the base GetMethod of the class, but in the Set part I first call the raiseEvent (It is loaded with the MethodBuilder of OnPropertyChanged generated method), then I call the setter part of the base class.

The result permits me to run this code.

INPCEmit emitter = new INPCEmit(); Type generated = emitter.CreateType("ATEST", typeof(Customer)); Object instance = Activator.CreateInstance(generated); Customer c = (Customer)instance; c.Property = "TEST"; INotifyPropertyChanged inpc = (INotifyPropertyChanged)instance; inpc.PropertyChanged += delegate(Object sender, PropertyChangedEventArgs args) { Console.WriteLine("PropertyChanged:" + args.PropertyName); }; c.Property = "TEST"; c.AnotherProp = 22;

As you can see I generate the dynamic type with the methods I explained before, then I can create an instance with Activator.CreateInstance, then I can cast it to a Customer (it is a derived class), but I can cast also to a INotifyPropertyChanged and register for the event. The output shows that the dynamic class correctly implements INotifyPropertyChanged

All the code can be found here.

alk.

Tags:

Published by

Ricci Gian Maria

.Net programmer, User group and community enthusiast, programmer - aspiring architect - and guitar player :). Visual Studio ALM MVP

5 thoughts on “Implement INotifyPropertyChanged with dynamic code generation”

  1. I am curious if its possible to port this code to Silverlight. It would be HUGE if you could. I’ve tried a dump port and it doesn’t work, but I don’t really know what you are doing here. If you could try it…HUGE

  2. I do not think that dynamic code generation is the solution to implement interfaces such as INOtiryPropertyChabnged, this post is intendend mainly to show how technically you can do this.

    I had little experiences with silverlight. but I setup a simple scenario
    1) a WCF service that exposes all the functions needed to manage the Domain, all the objects are DTO.
    2) I create a service reference from silverlight (This in turn creates proxy for the DTO that implements IPRopertyChanges
    3) I setup silverlight application to work with dto, the user modify the objects, does operations and whenever he want he can press save.
    4) The save button simply takes all the DTO and send them to the appropriate update function in the service.
    5) The service does all the logic to update data.

    Moreover silverlight runs in browser, and it does not have full trust, so certain operations are not permitted, I think code generation is one of these :(, but I have not tried.

    Alk.

  3. Quite a late reply, I know. 🙂

    I succesfully used this example in a Silverlight application. There was however one thing that I had to change.

    Instead of raising the event and setting the new value, I had to set the new value and _then_ raise the event.

    If if for instance editted a field in a datagrid bound to a property of a dynamically created object, it would change the displayed value back to the old value.

    If you change
    gen.Emit(OpCodes.Ldarg_0);
    gen.Emit(OpCodes.Ldstr, pinfo.Name);
    gen.Emit(OpCodes.Call, raiseEvent);
    gen.Emit(OpCodes.Ldarg_0);
    gen.Emit(OpCodes.Ldarg_1);
    gen.Emit(OpCodes.Call, pinfo.GetSetMethod());
    gen.Emit(OpCodes.Ret);
    to:
    gen.Emit(OpCodes.Ldarg_0);
    gen.Emit(OpCodes.Ldarg_1);
    gen.Emit(OpCodes.Call, pinfo.GetSetMethod());
    gen.Emit(OpCodes.Ldarg_0);
    gen.Emit(OpCodes.Ldstr, pinfo.Name);
    gen.Emit(OpCodes.Call, raiseEvent);
    gen.Emit(OpCodes.Ret);

    it works like a charm!

  4. Yeah, you are right, NotifyPropertyChanged must be raised after the property has chantged and not before 🙂 thanks for pointing it out.

    alk.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.