Being a Delphi programmer from way back (14 years) and new to C#, I was
wondering if someone would be kind enough to explain how to create eventsin
C# for non-visual controls/classes.
In Delphi, it's similar to..
procedure TMyNewEvent (Sender: TObject; MyStream: TStream; var MyFlag:
boolean) Of Object;
In the containing class, it looks sort of like..
MyClass = class(TObject); // TPersistent or any TObject based class..
private
FOnMyEvent: TMyNewEvent;
protected
procedure DoMyEvent(Sender: TObject; AStream: TStream; var AFlag:
boolean);
property OnMyNewEvent: TOnMyEvent Read FOnMyEvent Write DoMyEvent;
published
..
end;
And is then written as..
procedure MyClass.DoMyEvent(Sender: TObject; AStream: TStream; var AFlag:
boolean);
begin
// do something here..
If Assigned(FOnMyEvent) Then
FOnMyEvent(Self, AStream, AFlag);
end;
In C#'lish, what would it look like?
I'm guessing callbacks/delegates or something similarly foreign to me.
I've only been looking at C# a few weeks and its similarities to Delphi are
really quite remarkable in some areas..but, not this one.
The good news for you is that C# is designed by the same man that
designed Delphi's Object Pascal, so in many ways its object model can
be seen as building upon and improving that of Delphi (though some
stuff, such as virtual class methods & constructors, is notably
lacking). For events specifically, Delphi's events weren't first-class
- they were just pointers to object methods exposed as properties on
objects. The obvious deficiency of that model was that only one
handler could be registered for the event at a time. The usual
pattern, as I recall, was for the client to get the old handler before
registering his new one, remember it, and call the old handler for the
new one - which, of course, requires all participants to be trusted,
as anyone can break the chain for any reason.
In C#, the equivalent of a "procedure ... of object" type is a
delegate type. So, for something like:
type TEventHandler = procedure(sender: TObject; args: TEventArgs) of
object;
you write instead:
delegate void EventHandler(object sender, EventArgs args);
There are three differences. First, unlike "procedure of object", an
object of a delegate type can represent a static method as well as an
instance one. Second, a single delegate object can represent several
methods at the same time - you can take any two delegate values and
combine them using operator + to get a new delegate value which, when
invoked, will invoke the arguments to + in order they were specified.
There are some subtleties there with return values and out/ref
arguments that's covered in MSDN in more detail. And third, delegate
types (and, in fact, all other named types) in .NET use nominal rather
than structural equivalence; this means that two delegate types are
always considered distinct, even when they have identical signatures,
and so values of those types won't be compatible. This is similar to
what you get in Delphi when you use the "type X = type ..." syntax.
With that, you can already write the equivalent of the original Delphi
declaration:
private FMyEvent: TEventHandler;
public property MyEvent: TEventHandler read FMyEvent write FMyEvent;
a direct C# translation will be:
private EventHandler _myEvent;
public EventHandler MyEvent
{
get { return _myEvent; }
set { _myEvent = value; }
}
This works, and it even allows to register several event handlers,
unlike the Delphi solution above. However, we still have an "untrusted
client" problem - any piece of code can mess with the event handler
list of the component in arbitrary ways - enumerate it and call
registered methods, clear it, rearrange methods in it, etc. C# offers
solution to this problem in form of first-class event members. You
declare one as follows:
public event EventHandler MyEvent;
This does mostly the same thing as the code before it, but with one
crucial difference - the code outside the class with the event can
only use operators += and -= to register and unregister handlers for
the event. It cannot raise the event, or modify the handler list in
arbitrary ways - "register" and "unregister" are the _only_ allowed
client operations. On the other hand, the code inside the class can
treat the event just like a private delegate-typed field (which it
really is), so it has full control.
In practice, there's more to it - operators += and -= invoked outside
the class don't actually read/write to field directly, but rather use
the special "add" and "remove" methods associated with the event (just
as property access uses "get" and "set" methods). For the event
declaration above, they are auto-generated, but you can override this
behavior and take full control:
private EventHandler _myEvent;
public event EventHandler MyEvent
{
add { _myEvent += value; }
remove { _myEvent -= value; }
}
though this isn't used all that often - usually either to delegate the
event to another object, or to optimize storage by avoiding a separate
field for every event (when your class has several dozen, as e.g.
System.Windows.Forms.ListBox, it can matter).
I think that should cover it for most purposes, but it is probably a
good idea to visit the links given by Kerem and Peter to learn all
there is to events & delegates in .NET.