As much as I know how awful the book you're using is, I have a feeling
that you may be leaving out some important typographical information in
posting your question, when you quote the book. And I sure hope that the
book isn't actually spelling the word "delegete".
In any case, there is no point in raising the event (invoking the event's
delegate field) more than once. So you would never see a method that
raises an event by having _both_:
if (MyEvent != null)
MyEvent(this, EventArgs.Empty);
and…
EventArgs e = new EventArgs();
if (handler != null)
handler(this, e);
Of course, in the second version, "handler" is not even declared.
In general, there are a few things that have to be done when raising an
event:
• Make sure that there's at least one subscriber to the event to invoke.
This is done, for example, by checking for a null delegate reference.
• Obtain values for all of the arguments that will be passed to the
event handler delegate being invoked. For the standard .NET pattern, you
already have "this" and the remaining parameter will an instance of some
class that inherits EventArgs (including, in the case of EventHandler or
EventHandler<EventArgs>, EventArgs itself).
• Actually invoke the non-null delegate reference representing
subscribers to the event.
For code that does not need to be thread safe, the following is
sufficient:
public event EventHandler MyEvent;
void OnMyEvent()
{
if (MyEvent != null)
{
MyEvent(this, EventArgs.Empty);
}
}
Note that there is no point in code that creates a new instance of
EventArgs, as in your second example. The EventArgs class has no data
members to set, so the EventArgs.Empty property always returns an instance
of EventArgs that is suitable for raising any event declared with
EventHandler or EventHandler<EventArgs>.
For code that does need to be thread safe, it is common to see this
instead:
void OnMyEvent()
{
EventHandler handler = MyEvent;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
The assignment/initialization of "handler" is atomic, so is thread safe.
And once the delegate instance is being referenced by the local variable,
the code is guaranteed the reference won't change between the time it's
compared against "null" and the time it's actually used for the delegate
invocation (as it could in the first example).
Perhaps your book is somehow trying to illustrate this thread-safe pattern
where you are looking.
Finally, either of the above approaches will work for user-defined event
types. It's simply a matter of using the right data for the delegate
invocation arguments. When implementing the standard .NET event pattern,
this may mean passing to the method some data that is then used to
initialize an instance of a class that inherits EventArgs. For other
event signatures, it's the same except that the data is used either
directly as the invocation parameters or is stored in some other data
structure used for the invocation signature.
Pete