Unity - Part 7: Adding Interfaces

Updated: thanks, Thomas Levesque (@thomaslevesque)!

Another post on the Unity series, this time, adding interfaces to proxies. Previous posts here (Registration by Convention), here (Injecting Values), here (Extensions), here (Aspect-Oriented Programming), here (Dependency Injection) and here (Introduction).

Unity can be used to inject additional interfaces to proxies generated for some type. We can easily add these interfaces at registration time but then we need to implement a behavior that can intercept calls to these interfaces and act accordingly.

A typical example is implementing INotifyPropertyChanged. Let’s see how we can register a type with this additional interface, first, by XML:

   1: <register type="MyNamespace.IMyInterface" mapTo="MyNamespace.MyClass">
   2:     <addInterface type="System.ComponnetModel.INotifyPropertyChanged"/>
   3: </register>

And by code:

   1: unity.RegisterType<IMyInterface, MyClass>(new InjectionMember[] { new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<NotifyPropertyChangedInterceptionBehavior>(), new AdditionalInterface<INotifyPropertyChanged>() });

Do note that I am explicitly setting the interceptor as InterfaceInterceptor, other ways exist, see part 3 of this series.

Now, let’s see the NotifyPropertyChangedBehavior class:

   1: public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
   2: {
   3:     var result = getNext()(input, getNext);
   4:  
   5:     if (input.MethodBase.IsSpecialName == true)
   6:     {
   7:         if (input.MethodBase.Name == "add_PropertyChanged")
   8:         {
   9:             this.handlers += (input.Arguments[0] as PropertyChangedEventHandler);
  10:  
  11:             result = input.CreateMethodReturn(null, input.Arguments);
  12:         }
  13:         else if (input.MethodBase.Name == "remove_PropertyChanged")
  14:         {
  15:             this.handlers -= (input.Arguments[0] as PropertyChangedEventHandler);
  16:  
  17:             result = input.CreateMethodReturn(null, input.Arguments);
  18:         }
  19:         else if (input.MethodBase.Name.StartsWith("set_") == true)
  20:         {
  21:             this.handlers(input.Target, new PropertyChangedEventArgs(input.MethodBase.Name.Substring(4)));
  22:         }
  23:     }
  24:  
  25:     return (result);
  26: }

Finally, sample usage:

   1: var component = unity.Resolve<IMyInterface>();
   2: var npc = component as INotifyPropertyChanged;
   3:  
   4: npc.PropertyChanged += delegate(Object source, PropertyChangedEventArgs args)
   5: {
   6:     //raised with args.PropertyName = MyProperty
   7: };
   8:  
   9: component.MyProperty = "Some Value";  //raises PropertyChanged event

Still lots of things to talk about Unity, stay tuned for more! Winking smile

                             

6 Comments

  • This doesn't seem to work... the cast to INotifyPropertyChanged fails (tried using code registration). Is something missing?

  • Hi, Thomas!
    Don't know exactly how is your code, but it is working on my machine... if you want, I can have a look at it.

  • OK, got it... you have to call unity.AddNewExtension<Interception>() before registering the type.

  • :-)
    Great! Keep dropping by and sending your comments/questions/complaints/etc!

  • Actually, there *is* something broken in your interception behavior... It only works if you set a property or subscribe/unsubscribe to the PropertyChanged event. All other calls fail, because Invoke returns null. I fixed it by changing the last statement of Invoke to this:

    return result ?? getNext()(input, getNext);

    It works in this case, but I'm not sure it's the correct correct way to do it...

  • Thomas,
    Thanks! You did find a bug!
    I fixed it as this:

    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
    var result = getNext()(input, getNext);

    if (input.MethodBase.IsSpecialName == true)
    {
    if (input.MethodBase.Name == "add_PropertyChanged")
    {
    this.handlers += (input.Arguments[0] as PropertyChangedEventHandler);
    result = input.CreateMethodReturn(null, input.Arguments);
    }
    else if (input.MethodBase.Name == "remove_PropertyChanged")
    {
    this.handlers -= (input.Arguments[0] as PropertyChangedEventHandler);
    result = input.CreateMethodReturn(null, input.Arguments);
    }
    else if (input.MethodBase.Name.StartsWith("set_") == true)
    {
    this.handlers(input.Target, new PropertyChangedEventArgs(input.MethodBase.Name.Substring(4)));
    }
    }
    return (result);
    }

    Will update the post soon. Again, thanks! ;-)

Add a Comment

As it will appear on the website

Not displayed

Your website