More about the event class
In Popular patterns around events?, several folks mentioned that a usage example would be a good idea. As I put one together, I found some small changes to make to the code. Just goes to show you that thinking about your consumer is a good idea.
Since this is a class for people writing classes, there are actually two consumers to consider: the user of my event class, and the user of the class you’re writing.
My sample code is based on you writing a custom control. You want it to fire an event before & after painting. A consumer should be able to write:
MyControl myControl = new MyControl();
myControl.PaintEvents.Before += delegate { Console.WriteLine("painting has begun"); };
myControl.PaintEvents.After += delegate { Console.WriteLine("painting has completed"); };
myControl.Invalidate();
I expect Invalidate to cause a paint to happen.
Here’s the code for the control:
class MyControl
{
readonly MyEvent<EventArgs> _paintEvents;
public IRestrictedMyEvent<EventArgs> PaintEvents { get { return _paintEvents; } }
public MyControl()
{
this._paintEvents = new MyEvent<EventArgs>(this);
}
public void Invalidate()
{
using (_paintEvents.Open(new EventArgs()))
{
// do your painting here
}
}
}
As mentioned, I modified the previous code a little. So, here’s the updated version:
// IMO, this belongs as a nested type in MyEvent, called IRestrictedView.
// C# doesn't support inheriting from nested types, doh!
interface IRestrictedMyEvent<T> where T : EventArgs
{
// MS guidelines say "Consider naming events with a verb. "
//
// I don't see how to follow that here. Perhaps "Raising" and
// "Raised"?
event MyEvent<T>.Handler Before;
event MyEvent<T>.Handler After;
}
// nested type
partial class MyEvent<T> where T : EventArgs
{
// MS guidelines say "Use an EventHandler suffix on event handler names."
//
// The full name of this type is "MyEvent.Handler", which seems to match
// the guidance.
public delegate void Handler(object sender, T e);
}
// IRestrictedMyEvent implementation
partial class MyEvent<T> : IRestrictedMyEvent<T>
{
public event Handler Before;
public event Handler After = delegate { };
}
// Overridable methods to fire the events.
// It's not clear to me how these are useful, but people have asked for them
partial class MyEvent<T>
{
protected virtual void OnBefore(T e)
{
this.Before(this._sender, e);
}
protected virtual void OnAfter(T e)
{
this.After(this._sender, e);
}
}
// relationship with owner, so I can pass the right sender
partial class MyEvent<T>
{
object _sender;
public MyEvent(object sender)
{
this._sender = sender;
}
}
// Public API
partial class MyEvent<T>
{
T _e;
public IDisposable Open(T e)
{
this._e = e;
this.OnBefore(e);
return this;
}
public void Close()
{
this.OnAfter(_e);
this.DebugOnClose();
}
}
// IDisposable
partial class MyEvent<T> : IDisposable
{
void IDisposable.Dispose()
{
this.Close();
}
// add this if you need to call Dispose on an instance of MyEvent
//public IDisposable IDisposable { get { return this; } }
}
// DEBUG verification
//
// In DEBUG builds, make sure that Dispose/Close really is called.
// If you don't, an assert will fire when the GC runs.
partial class MyEvent<T>
{
[Conditional("DEBUG")]
void DebugOnClose()
{
GC.SuppressFinalize(this);
}
// You can't put a Conditional attribute on a destructor!
// [Conditional("DEBUG")]
#if DEBUG
~MyEvent()
{
Debug.Fail("MyEvent was not properly disposed");
}
#endif
}
Comments
Anonymous
May 31, 2009
PingBack from http://outdoorceilingfansite.info/story.php?id=4083Anonymous
May 31, 2009
PingBack from http://outdoorceilingfansite.info/story.php?id=21719