|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Warning.While reading this article you should keep in mind that I rather freely operate some terms and use:
as synonyms and:
also as synonyms. Although in general it's not true, in the context of the article I assume it is. Introduction.One of the things that still excites me most in Microsoft's .Net Framework is the delegates. They make a programmers life a lot easier when it comes to implementing various callbacks and notifications. I have some experience in Java (though a bit outdated) and I remember my frustration over the Java Beans event model. To make an object notify listeners about the object's state changes you have to declare an event callback interface, implement it in the listeners and keep track of them somehow. While designing of the interface you have to decide how many callback methods should the interface declare because if later on your decide to add a couple more notifications you'll have to create another callback interface and, possibly, rewrite all your listeners. At times like that I had been recalling good old C function pointers. And then Microsoft introduced the delegates. A delegate is essentially a type-safe object-oriented method pointer. A delegate can be singlecast, thus it points at a single method of a single object (very good for callbacks), or multicast thus pointing at many methods of various objects or even at many methods of a single object (very good for event notifications). Although delegates are classes, you can do a little without help of the compiler. You can declare new delegate types with special syntax, create delegate instances, assign, add and subtract them and you can invoke them in turn invoking their target methods. You cannot subclass delegates (they all are sealed). But to tell the truth - do you really need it? Observable property design pattern in .Net.The delegates take crucial part in notification scenarios. For instance, you can notify some object that a value of a property has been changed. Microsoft in the documentation on Windows Forms programming suggests following pattern for controls and components:
Although this code seems to be verbose, in more complex scenarios this
verbosity pays off, because the pattern is very flexible. Windows Forms library
extensively uses it to notify the forms about their child controls state
changes. For instance, the You can view a property and it's change event as a single entity very similar
to the Smalltalk's The good thing is that you can bind the the property/event pairs and UI
elements with the standard data binding (NB: it is more oriented on Also note that in the Memory leaks.But you have to pay for everything, especially for comfort. While this "live
update" scenario is very nice and convenient, there is one annoying thing - you
have to track all your event subscriptions. If your form registers a handler for
The sad thing is that you may really get used to the fact that CLR manages memory. The code generated by UI designers never unregisters event handlers. All this might make you think that the delegates work in "fire and forget" fashion. Partial solution number 1.Suppose you have an event source object that exposes a lot of events and many event sink objects that subscribes for many of them. When the event source goes out of scope, it is a good idea to unsubscribe all event sinks at once. It can easily be done. If you event is declared like: public event EventHandler PropertyChanged;
you should change this declaration to: EventHandler propertyChanged;
public event EventHandler PropertyChanged
{
add
{
propertyChanged += value;
}
remove
{
propertyChanged -= value;
}
}
Why would you do that? Because if some objects have subscribed for the public void Unsubscribe()
{
propertyChanged = null;
}
Simple, isn't it? In case you use co-called optimized event implementation with the Partial solution number 2.Suppose the scenario above doesn't suite you needs and you have to unsubscribe only one observer from all the events exposed by the observable. Let's the event source manage the removal. First, we need a simple utility class: public sealed class DelegateUtil
{
. . . . .
public static Delegate Unsubscribe(Delegate del, object target)
{
if(null == del)
return null;
if(null == target)
return del;
Delegate[] list = del.GetInvocationList();
for(int i = 0; i < list.Length; i++)
{
if(target == list[i].Target)
list[i] = null;
}
return Delegate.Combine(list);
}
. . . . .
}
The public void Unsubscribe(object target)
{
Value = (EventHandler)DelegateUtil.Unsubscribe(Value, target);
}
If you class exposes other events, repeat Weak delegates.If the event sink object does not know exactly when it goes out of scope,
i.e. it is neither a control, nor a component, nor it implements the But is it possible to somehow emulate weak delegates at all? Of course! Greg Schechter in his blog presents an implementation of classes very close to weak delegates: http://blogs.msdn.com/greg_schechter/archive/2004/05/27/143605.aspx [^]. Actually I highly recommend to read his post, he describes the problem with event delegates very thoroughly and with great diagrams that make the picture much clearer. He introduces an object that decorates a delegate making it behave as if it
was a weak one (Greg calls it the Ian Griffit in his blog answers to Greg's call and tries to implements a generic weak delegate: http://www.interact-sw.co.uk/iangblog/2004/06/06/weakeventhandler [^]. The problem with his code is that he keeps a weak reference to a usual delegate, so the delegate and the observer object get garbage collected too soon. Xavier Musy offers a working solution:
http://www.seedindustries.com/blog/x/2004_06_01_archive.html [^].
He essentially reimplements the Weak delegate decorators factory.In my opinion, Greg Schechter's solution is almost perfect. The only problem with it is that the decorator must know too much about both the observable and the observer. It has to know at least their types, the event and the event handler. So you cannot make it generic, even in .Net 2.0, and have to reimplement it for each event of each event source class. But if you take a close look at the Greg's code you'll notice that the implementation of the decorator is simple, if not trivial. The finalizer there is just for sample, so you need a constructor and the method with signature identical to the target method. If it is that simple, it would be easy enough to create such objects on the fly. Do you remember the word "thunk"? Enter
In the scenarios 2 and 4 it doesn't matter what kind of delegate we are
using, the delegate target lives as long as the application domain lives. So we
basically need to consider scenarios 1 and 3. The scenario no. 1 is related to
the MVC pattern and data binding, the scenario no. 3 is very well known to
anyone who ever (mis)used the Let's imagine that we have an event source object with property public class WeakInstanceToInstance: WeakReference
{
public WeakInstanceToInstance(EventSink observer):
base(observer)
{}
public void Handler(object sender, EventArgs e)
{
EventSink observer = (EventSink)Target;
if(null != observer)
observer.OnEvent(sender, e);
else
{
EventSource observable = sender as EventSource;
if(null != observable)
observable.Event -= new EventHandler(Handler);
}
}
}
This class or more specific, it's translation to IL instructions, is going to be our template for scenario 1. Let's imagine how an object, probably a form, gets wired to public class WeakStaticToInstance: WeakReference
{
public WeakStaticToInstance(EventSink observer):
base(observer)
{}
public void Handler(object sender, EventArgs e)
{
EventSink observer = (EventSink)Target;
if(null != observer)
observer.OnIdle(sender, e);
else
EventSource.StaticEvent -= new EventHandler(Handler);
}
}
The code for scenario 3 is a bit simpler but they share a lot. While Microsoft uses and recommends to use for events delegates signatures
two parameters, the first of type Given proper info, the overloaded method public static Delegate Decorate(Type eventSourceType, string eventName,
Delegate del)
public static Delegate Decorate(object eventSource, string eventName,
Delegate del)
public static Delegate Decorate(object eventSource, string eventName,
object eventSink, string eventHandlerName)
public static Delegate Decorate(Type eventSourceType, string eventName,
object eventSink, string eventHandlerName)
public static Delegate Decorate(EventInfo eventInfo, Delegate del)
public static Delegate Decorate(EventInfo eventInfo, object eventSink,
string eventHandlerName)
You usually decorate event delegates by replacing code like: eventSourceObject.Event +=
new SomeEventHandler(eventSinkObject.EventHandlerMethod);
with: eventSourceObject.Event +=
(SomeEventHandler)WeakDelegateDecorator.Decorate(eventSourceObject,
"Event", eventSinkObject, "EventHandlerMethod");
or with more type safe (and memory consuming) version: eventSourceObject.Event +=
(SomeEventHandler)WeakDelegateDecorator.Decorate(eventSourceObject,
"Event", new SomeEventHandler(eventSinkObject.EventHandlerMethod));
I wanted to make the These Keep in mind that the event handler methods must be public. This is because in .Net a class that resides in another assembly cannot access non-public memebers of your class. It is a limitation of the weak delegates and I'm not aware of any workaround. Each decorator class has two methods - a constructor and an event handler. The constructor method is simple, it just passes the reference the the
event sink object to the base class (the I add the tailcall prefix to some call/callvirt
instructions: to the call to the event target method and to the Yet another optimization is use of predefined MSIL instructions for basic
value types. For a generic value types, even for low-level I have changed the My implementation of weak decorators differs from Greg's. Greg's containers/observers keep references to "weak containers" and attach events of many observables to a single mediator that relays the events to the observer (I've redrawn Greg's diagram):
In my implementation the observes and the observables do not know about mediators at all so I have to create a new decorator class for each new event/handler combination:
Greg's approach is better in terms of memory consumption. I considered to use .Net 2.0 dynamic methods as a lightweight replacement for the decorators, but it seems to me that they don't fit in the picture. Please, prove me wrong, it'd be nice to get rid of this clunky in-memory type cache. If you ask me why I don't use generic hash tables, I have two answers. First, the code is written for .Net 1.1. Second, just imagine yourself the declaration of the cache with generics. I've introduced a set of new tests that prove the weak delegate decorators work for .Net 2.0 generic delegatess. I had been worrying a bit that I'd have to change or tweak the IL code generation for generics somehow. To my surprise the code written for .Net 1.1 worked with generics without any change. It means that Microsoft's developers had done a amaising piece of job seamlessly integrating generics into CTS and CLR. Excellent! By the way, after writing tests for generic delegates I don't consider declarations of reflection-based factory methods ugly: declarations and casts for more or less complex generic delegates are uglier. Further improvements.My code generates a lot of types, it would be a good idea to generate as many event handlers per decorator as possible to reduce the number of types and the total number of decorators. My code is inherently not thread safe; it would be nice to make it so. If the IL code generator throws an Sample projects.As usual, my sample project is created in SharpDevelop IDE. The project contains five subprojects: Pvax.WeakDelegates, Pvax.WeakDelegates.Tests, WinFormsIdleTest, ConsoleTest and UnsubscribeTest. I've also converted them to VS-compatible projects with #D 2.0. The first two are the weak decorators library itself and the tests assembly for the library. The rest are described below. The sample project folder also contains FxCop and NUnit project files. FxCop
fairly says that the Pvax.WeakDelegates namespace contains too little classes,
but keep in mind that all decorators are emitted in this namespace also. It also
is not happy about static constructors and UnsubscribeTest sample project.This sample consOLe application illustrates the WinFormsIdleTest sample project.The WinFormsIdleTest is a sample of the weak delegates usage in a Windows
Forms application. I hook child forms to the The sample application consists of two forms. The main form:
has three buttons. By clicking the New sticky form... button you create a small form:
It performs a simple animation using If you look at the code, you'll see that the child form constructor allocates an array of bytes to Simulate the real application's data. I allocate a 100 Kbytes from the large heap, because the memory manager considers objects that big "large". It is very convenient to watch these allocations using the Performance Monitor application:
Add, for instance, following counters:
If while running the program, you should create a couple dozens of child forms with the New sticky form... button, you should see a picture similar to:
Now close all the child forms. Their respective
I've marked with light green line the moment I clicked the button. As you see, the size of the large heap doesn't change and we obviously have a memory leak here. Restart the sample application and repeat all the steps except this time
click the New weal sticky form... button. The event handler for this
button is almost the same, but it uses my
Now close all the child forms and click Force GC! button. After that the graph should look like:
Again, I've marked with light green line the moment I clicked the button. As you can see, with weak event decorators the memory gets reclaimed as it should be. ConsoleTest project.This sample project illustrates the property observer and constrained
property patterns. It tests a way to unsubscribe all the observers from the
events at once. It works because the It also tests the weak delegates decorators. An interesting detail: at weak decorators tests you are going to see that while the event sink objects get garbage collected, the decorators themselves stay in memory up until the moment when the code raises the events again. Only at that moment the decorators become aware that their targets are not reachable anymore and unsubscribe themselves from the source events. This tests show how much slower the decorated delegates are in comparison with undecorated. You can play with various constants like It is interesting to run the ConsoleTest under
CLR Profiler [^].
To minimize the memory stress, set the Lessons learnt.Because the delegates are immutable, using MSIL and You know it, but I repeat - unit tests are useful. They helped me to find and squash two nasty bugs in the IL code generator. Both bugs were related to value objects that the decorators' event handlers had to return. Conclusion.Delegates are great, no doubt. Their use makes the source code clearer and
object models of our applications more flexible. There is, however, a flip side
of this coin. We cannot get rid of it, but we can simplify our tasks with License.The Links.
History.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||