Delphi type events in C#

J

JM

Hi,

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 events in
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.

Thanks in advance.

John McTaggart
 
P

Pavel Minaev

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.
 
P

Peter Morris

Hi JM

You know when you later change your method signature you have to go to every
line of code in your app that uses the event type and change its paramters
too? The standard pattern in .NET is to always have only 2 parameters.

01: Sender
02: A specific object type with all the data you need
procedure TMyNewEvent (Sender: TObject; MyStream: TStream; var MyFlag:
boolean) Of Object;

So in C# the additional data you need is a stream and a boolean.

public class MyEventArgs : EventArgs
{
public readonly MemoryStream MyStream;
public bool MyFlag;

public MyEventArgs(MemoryStream stream, bool myFlag)
{
//check stream is not null, throw an exception if it is
MyStream = stream;
MyFlag = myFlag;
}
}

Now you can declare an event like this

public event EventHandler<MyEventArgs> NameOfEvent;

In Delphi the standard was to name the event OnXXXX and the trigger method
DoXXXX, in .NET the standard is to name the event XXXX and the trigger
method OnXXXX.

Delphi
Event name = OnClick
Method name = DoClick

.NET
Event name = Click
Method name = OnClick

The method would be

protected virtual void OnClick(MyEventArgs e)
{
EventHandler<MyEventArgs> eventToCall = Click;
if (eventToCall != null)
eventToCall (this, e);
}

The reason I store the event before checking not null is just an old habit,
it's in case some secondary thread removes the last event registration (and
therefore causing it to become null) between checking not null and executing
it.


The one other big difference in coding for .NET that I can think of is when
Delphi you would create a CustomXXXX component with all of its properties
protected, and then descend an XXXX component redeclaring the relevant
protected properties as published. In .NET you can't redeclare the
visibility of a member so instead you skip the CustomXXXX class completely
and in any new classes where you don't want a property you would hide it
instead.

[Browsable(false)]
public override bool IsActive
{
get { return base.IsActive; }
set { throw new ...................; }
}
 
J

JM

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.

Pavel,

First, I really appreciate you and everyone elses responses. I'm trying to
wrap my head around all of the differences between the 2 languages and
frameworks.
The good news for you is that C# is designed by the same man

I kind of knew that. :0)

I remember reading the stories about MS sending the lunchtime limos to
Borland's campus trying to poach some of their engineers..I'm thinking MS
might have seen the potential of a framework like Delphi's VCL being the
common denominator among multiple languages that wasn't based on
Java..which, to MS is basically just another 4 letter word..
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.

Why is that a deficiency? To me, simplicity adds clarity and readability
while reducing bugs.

I've been looking at unicast-vs-multicast and I still can't quite see the
advantage of multicast beyond maybe late binding and allowing other objects
to react to the event.

Are those the main advantages?

Thanks!

John McTaggart
 
P

Pavel Minaev

Why is that a deficiency? To me, simplicity adds clarity and readability
while reducing bugs.

Simply put, it has the same problem as exposed public fields - it
breaks encapsulation.

I've been looking at unicast-vs-multicast and I still can't quite see the
advantage of multicast beyond maybe late binding and allowing other objects
to react to the event.

Allowing several objects to react to the event is indeed the key
advantage of multicast. There really isn't any reason why there should
be only one handler for the event - after all, even the classic
Observer pattern allowed for several observers...
 
J

JM

I've been looking at unicast-vs-multicast and I still can't quite see the
Allowing several objects to react to the event is indeed the key
advantage of multicast. There really isn't any reason why there should
be only one handler for the event

Pavel,

Like I said, I'm not really new to the realm of OO, just the way C# does it.

Just to make sure that I have this down, is the following psuedo code
correct?

If I have control1, control2 and control3 and each subscribes to event1,
then when event1 fires, each of the controls would be notified? Are the
events asynchronous - basically fire and forget? Are the controls notified
in the order in which they initially subscribed to the event? I'm guessing
the delegate/event has to contain some sort of stack, queue (or more
probably list) of subscribers and the logic to control them - yes?

Sorry for all the questions, but I'm trying to solidify this in my brain.

Next up, streams with readers/writers attached! :)

With Delphi, you create the stream and then attach it to the reader/writer
via the reader/writer's constructor. Then, when done, you have to destroy
the reader/writer and then the stream. If you destroy the stream first, then
the reader/writer ends up with a dangling pointer to the stream and kaboom -
your basic memory leak or much worse..

AStream := TMemorystream.Create;
Writer := TWriter.Create(AStream, 1024); // 1024 byte buffer
With Writer Do Try
WriteBoolean(true);
WriteInteger(-1);
// etc
Finally
Writer.Free; <- first
AStream.Free; <- second
End;

Does this hold true in C# as well?

Thanks again

John McTaggart
 
P

Pavel Minaev

Just to make sure that I have this down, is the following psuedo code
correct?

If I have control1, control2 and control3 and each subscribes to event1,
then when event1 fires, each of the controls would be notified?

Yes, though technically, the subscribers are not controls, but
specific methods.
Are the events asynchronous - basically fire and forget?

No, handlers are executed synchronously (though really it's up to the
component - it can, of course, enumerate the list of handlers, and
spawn an individual thread for each).
Are the controls notified in the order in which they initially subscribed to the event?
Yes.

I'm guessing the delegate/event has to contain some sort of stack, queue (or more
probably list) of subscribers and the logic to control them - yes?

Yes, a multicast delegate is essentially just a list, where each
element is the receiver - or null for static methods - and pointer to
actual method code.
Next up, streams with readers/writers attached! :)

With Delphi, you create the stream and then attach it to the reader/writer
via the reader/writer's constructor. Then, when done, you have to destroy
the reader/writer and then the stream. If you destroy the stream first, then
the reader/writer ends up with a dangling pointer to the stream and kaboom -
your basic memory leak or much worse..

AStream := TMemorystream.Create;
Writer := TWriter.Create(AStream, 1024); // 1024 byte buffer
With Writer Do Try
   WriteBoolean(true);
   WriteInteger(-1);
    // etc
Finally
  Writer.Free;     <- first
  AStream.Free; <- second
End;

Does this hold true in C# as well?

No. When you do StreamWriter.Close() (or use the using-block, which is
what you normally do), it automatically closes the underlying stream.
If you close the stream first, then the writer will of course be
unusable, but there won't be any dangling references or memory leaks -
no such thing in C# (thanks to GC).
 
P

Peter Morris

I've been looking at unicast-vs-multicast and I still can't quite see the
advantage of multicast beyond maybe late binding and allowing other
objects to react to the event.

I have one thing to say in answer to this.


TApplicationEvents.OnIdle :)
 
P

Peter Morris

No. When you do StreamWriter.Close() (or use the using-block, which is
what you normally do), it automatically closes the underlying stream.
<<

Often to my annoyance :)

If you close the stream first, then the writer will of course be
unusable, but there won't be any dangling references or memory leaks -
no such thing in C# (thanks to GC).
<<

Except ofcourse if the instance is reachable from some static class or
long-living instance. It's easy to get into the mindset that the GC solves
all of your memory-leak problems, but that's not the case.
 
P

Pavel Minaev

No. When you do StreamWriter.Close() (or use the using-block, which is
what you normally do), it automatically closes the underlying stream.
<<

Often to my annoyance :)

Not just yours... I imagine you have that "no-op Dispose" delegating
Stream wrapper in your toolbox too?
 
J

JM

If I have control1, control2 and control3 and each subscribes to event1,
Yes, though technically, the subscribers are not controls, but specific
methods.

I figured as much, I just used controls as examples..
No, handlers are executed synchronously (though really it's up to the
component - it can, of course, enumerate the list of handlers, and
spawn an individual thread for each).

Ironically enough, it was thread spawning that actually made me think to ask
the question in the first place..
Yes, a multicast delegate is essentially just a list, where each
element is the receiver - or null for static methods - and pointer to
actual method code.

I kind of figured that's how they worked.
No. When you do StreamWriter.Close() (or use the using-block, which is
what you normally do), it automatically closes the underlying stream.

Bummer. To me, that's an instance where the GC should just back off a
little.

Thanks!

John McTaggart
 
J

Jeff Johnson

Bummer. To me, that's an instance where the GC should just back off a
little.

It's not the GC, it's the StreamWriter class itself that closes the stream.
Unless Reflector is lying....
 
P

Pavel Minaev

Ironically enough, it was thread spawning that actually made me think to ask
the question in the first place..

Interestingly enough, .NET delegate types do provide some primitives
for async programming - in particular, they all have BeginInvoke
method that executes the referenced method on a separate thread, with
the ability to register callback for completion notification, and
polling. However, this only works for single-cast delegates.

Anyway, as mentioned before, you can get a list of single-cast
delegates from any multicast delegate via GetInvocationList() method,
and then deal with it as you see fit.

By the way, while we're talking about thread safety... I recall now
that one other feature of auto-generated event accessors is that they
are thread-safe with respect to the event handler list (i.e. two
threads can register/unregister handlers concurrently without breaking
things). This doesn't help as much as one would expect it to, because
the standard pattern used to raise events - "if (Click != null) Click
()" - is still not thread-safe between the check and the call.
Bummer. To me, that's an instance where the GC should just back off a
little.

This has absolutely nothing to do with GC, it's just how StreamWriter
is implemented - its Dispose() calls BaseStream.Dispose(), and there's
no way to override that easily, unfortunately (as noted earlier, a
possible hack is to feed the reader a customized Stream that delegates
all operations except for Close/Dispose to the wrapped real Stream).
 
J

JM

No. When you do StreamWriter.Close() (or use the using-block, which is
It's not the GC, it's the StreamWriter class itself that closes the
stream. Unless Reflector is lying....

I have no doubt it's telling the truth. If it's the StreamWriter class, then
it's making assumptions.

The problem is, it doesn't know if you're done with the stream - just that
you're done with the writer..

John McTaggart
 
J

Jeff Johnson

I have no doubt it's telling the truth. If it's the StreamWriter class,
then it's making assumptions.

The problem is, it doesn't know if you're done with the stream - just that
you're done with the writer..

Agreed. I wrote something that worked with streams and had several overloads
in the constructor. If it was passed a filename and therefore opened a
stream itself, it would later close the stream. But if it was given a
stream, then it was hands off! MS should have done the same with
StreamWriter.
 
J

JM

No. When you do StreamWriter.Close() (or use the using-block, which is
Agreed. I wrote something that worked with streams and had several
overloads in the constructor. If it was passed a filename and therefore
opened a stream itself, it would later close the stream. But if it was
given a stream, then it was hands off! MS should have done the same with
StreamWriter.


I used to do something similar with application settings. I'd open the
stream and then
attach/detach different readers/writers to that single stream saving and
loading settings.


John McTaggart
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top