MySelf said:
Dans son message précédent, Jeff Johnson a écrit :
EventArgs is a genric type, isn't it ? To use the data on it, I must cast
it to a more specific type, or not ?
Don't use the word "generic" to describe what you're trying to describe.
You're talking about EventArgs being a BASE class for all other classes that
pass arguments to event handlers. While it's okay in conversational English
to use "generic" this way, it is not good to use it this way in the context
of C#, because "generic" has a very specific meaning there.
Let's take a step back and talk about the .NET paradigm for event handlers.
First and foremost, .NET 1.0 introduced a new convention for the signature
of event handling methods, and all subsequent versions have followed this
convention religiously, and Microsoft has urged developers to do so as well.
In the days of VB, event handlers could have a variable number of
parameters, depending on what kind of data the event source wanted to pass
on to its subscribers. .NET said "This needs to change." The new direction
was to always pass exactly two parameters to any event handler. The first
parameter is always of type object and refers to the class instance that
raised the event. By convention it is named "sender." The second parameter
is always of type System.EventArgs or a class ultimately derived from
System.EventArgs, and by convention it is called e.
If any data needs to be passed to the event handler, it is passed in
properties of an EventArgs-derived class. That way event handler signatures
are always uniform, and look like (again, by convention)
<access method> void <object name>_<event name>(object sender,
<EventArgs-descended type> e)
Some events never need to pass any information. An example of this is the
Changed event of a text box. The text box doesn't tell you WHAT has changed,
merely that a change has occurred. If you need to know what changed, you
have to check the current value of the Text property against the previous
value (which you need to have saved earlier). Because no additional data
needs to be sent during a Changed event, the event delegate uses the base
System.EventArgs class as the type of e. System.EventArgs has no members
beyond what it inherits from Object. (Well, except for the static Empty
field; the point is it has no properties with which to pass data.)
If you need to pass additional information to an event then you need to
either use a Framework-provided EventArgs descendant or you need to roll
your own. But when you do this you really should change the declaration of
your event to use the new class. Let's just go to an example. I'm going to
create an event that needs a Progress property. Your code might look like
this:
public class ProgressEventArgs : EventArgs
{
// I'm leaving off any validation.
// Yes, at the moment you could set negative progress.
// Perhaps this class will be used for politicians....
public int Progress { get; set; }
public Progress() { }
public Progress(int progress)
{
Progress = progress;
}
}
Now for the class that will expose an event:
public class Something
{
// This syntax for declaring events was new as of Framework 2.0.
// I prefer it to the original delegate syntax and therefore I'm
// using it in my example.
event EventHandler<ProgressEventArgs> ProgressChanged;
// Code to raise the event is outside the scope of this discussion,
// so I omit it
}
The event is declared to use the type ProgressEventArgs. This will affect
the signature of the event handler method.
In the subscribing class you would write a handler that looks like this:
private void something1_ProgressChanged(object sender, ProgressEventArgs e)
{
if (e.Progress < 100)
{
// ...
}
}
Notice how the event handler uses the exact same class that was specified in
the declaration of the event. This is how your code should be written. What
you seem to think is the case, however, is that the event is declared like
this:
event EventHandler<EventArgs> ProgressChanged;
and that the event handler should look like this:
private void something1_ProgressChanged(object sender, EventArgs e)
{
ProgressEventArgs actualType = e as ProgressEventArgs;
if (actualType.Progress < 100)
{
// ...
}
}
That is completely wrong. If you ever have to write code like this then I
say something has been designed badly. I have never once seen code that
required me to cast the incoming e parameter, and despite what Pete
suggested, I think that in this particular case, the convention of an event
handler using a concrete class has been adopted 99.999999999% of the time.