Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
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