How do I isolate event-generating code in a derived class?

E

Elder Hyde

Hey all,

A class of mine needs to tell the outside world when its buffer is not
empty. The problem is that C# seems to force you to put the
event-raising code in the base class. To illustrate, consider what I'll
do in Java:

public interface DataAvailabilityListener extends java.util.EventListener {
void dataArrived(DataAvailabilityEvent event);
}

then, in my base class, I can do this:

public abstract class SmartQueue {
addDataAvailabilityListener(DataAvailabilityListener listener);
}

then in my implementation class, say a memory-backed smart queue, I can
do this:

public class MemorySmartQueue implements SmartQueue {
public void poolForData() {
// data available!
while(iter.hasNext()) {
DataAvailabilityListener lis =
(DataAvailabilityListener)iter.next();
lis.dataArrived(someEvent);
}
}
}

Simple and straighforward. However, consider a C# implementation: in the
interface, I may have something like this:

public abstract class SmartQueue {
public event DataAvailabilityEventHandler DataArrived;
}

Now, consider what I have to do in MemorySmartQueue:

public class MemorySmartQueue : SmartQueue {
public override void poolForData() {
// data available!
if(DataArrived == null) {
// BZZZZTTT!!! Can only do this in SmartQueue!
DataArrived(this, args);
}
}
}

I can't believe this. Either I'm missing a really obvious thing, or I
have to deal with this... this... awkward mechanism. Why the hell
doesn't it allow me to raise an event in the derived class? I don't want
to put any behaviour in my abstract class, I want to put just an interface!

Is there any way around this? (Plus I hope Whidbey will give us a Set
collection, dammit).

TIA!
Elder
 
C

Chris Taylor

Hi,

It is standard to implement a method to fire the event, this method is the
event name prefixed with 'On'. So in your case the base class would have a
OnDataArrived, which can be called from anywhere. This has the advantage of
not having to cluter your code checking if the event has any delegates
before firing and also gives the derived implementation an alternative and
better performing method of handling the event by overriding the On...
method.

public abstract class SmartQueue
{
public event DataAvailabilityEventHandler DataArrived;

// This is called to fire the event
public void OnDataArrived( YourEventArgs e )
{
if ( DataArrived != null )
DataArrived( this, e );
}
}

public class MemorySmartQueue : SmartQueue
{
public override void poolForData()
{
// data available!
OnDataArrived( args ); // Fire the event
}
}


Hope this helps
 
G

Guest

How can we perform an Async event in C#?

Events are Synchronous usually. Do we just call BeginInvoke, if so, how
would we do this?
 
E

Elder Hyde

Hi Chris,

So there's really no way of escaping this? The thing is that I want my
abstract class to be really just an interface, with no implementation.
The way I'd like to organize it is something like:

public abstract class SmartQueue {
// all interface stuff only, plus
// static methods
}

public abstract class AbstractSmartQueue : SmartQueue {
// put stuff that are common to base classes here.
}

public class MemorySmartQueue : AbstractSmartQueue {
// put memory-backed specific operations here
}

Now, I can't (or rather don't want to) make SmartQueue an interface,
because: I want to have static methods--it makes the most sense to put
them in SmartQueue. But anyway, OK, I know the way now. Thanks!

Regards,
Elder
 
C

Chris Taylor

Hi,

Mistake in my code, in my haste, I forgot to make the On.... virtual SORRY.

public virtual void OnDataArrived( YourEventArgs e )

Regards
 
E

Elder Hyde

Happened to me all the time.. I hadn't been typing "virtual" for about 7
years, since I left C++ behind ;)
 
C

Chris Taylor

Hi,

You could use BeginInvoke, and remember to always call EndInvoke. The events
will still be fired synchroneously, but in a separate thread. Something like
the following should do, this will be limited to only one handler however.

public virtual void OnDataArrived( EventArgs e )
{
if ( DataArrived != null )
DataArrived.BeginInvoke( this, e, new AsyncCallback(
DataArrivedCompleted ), null );
}

private void DataArrivedCompleted( IAsyncResult ar )
{
if ( DataArrived != null )
DataArrived.EndInvoke( ar );
}

You could also just use a separate thread to fire the event in, that way you
can have multiple handlers.

public virtual void OnDataArrived( EventArgs e )
{
if ( DataArrived != null )
System.Threading.ThreadPool.QueueUserWorkItem( new
System.Threading.WaitCallback( FireDataArrived ), e );
}

private void FireDataArrived( object e )
{
if ( DataArrived != null )
DataArrived( this, (EventArgs)e );
}

Then you can also fire each handler individually.

public virtual void OnDataArrived( EventArgs e )
{
if ( DataArrived != null )
foreach( DataAvailabilityEventHandler d in
DataArrived.GetInvocationList() )
{
d.BeginInvoke( this, e, new AsyncCallback(DataArrivedEventComplete),
d );
}
}

private void DataArrivedEventComplete( IAsyncResult ar )
{
DataAvailabilityEventHandler e =
(DataAvailabilityEventHandler)ar.AsyncState;
e.EndInvoke( ar );
}

Of course you would have to make sure the caller is aware of this
non-standard event calling and manage thread syncronization for shared data
etc. This code also lacks significant error handling, but should leave you
with a number of options.

Hope this helps
 
G

Guest

I would assume this is a rare case that we would need async events then if
its so much hassle.


I am basically trying to draw a similarity between Win32 PostMessage and
SendMessage. Usually I used PostMessage as a way to fire events rather than
SendMessage wheras on C# I am using the equivilent to SendMessage now.
 
D

Daniel O'Connell

Elder Hyde said:
Hey all,

A class of mine needs to tell the outside world when its buffer is not
empty. The problem is that C# seems to force you to put the
event-raising code in the base class. To illustrate, consider what I'll
do in Java:

public interface DataAvailabilityListener extends java.util.EventListener {
void dataArrived(DataAvailabilityEvent event);
}

then, in my base class, I can do this:

public abstract class SmartQueue {
addDataAvailabilityListener(DataAvailabilityListener listener);
}

then in my implementation class, say a memory-backed smart queue, I can
do this:

public class MemorySmartQueue implements SmartQueue {
public void poolForData() {
// data available!
while(iter.hasNext()) {
DataAvailabilityListener lis =
(DataAvailabilityListener)iter.next();
lis.dataArrived(someEvent);
}
}
}

Simple and straighforward. However, consider a C# implementation: in the
interface, I may have something like this:

public abstract class SmartQueue {
public event DataAvailabilityEventHandler DataArrived;
}

Now, consider what I have to do in MemorySmartQueue:

public class MemorySmartQueue : SmartQueue {
public override void poolForData() {
// data available!
if(DataArrived == null) {
// BZZZZTTT!!! Can only do this in SmartQueue!
DataArrived(this, args);
}
}
}

I can't believe this. Either I'm missing a really obvious thing, or I
have to deal with this... this... awkward mechanism. Why the hell
doesn't it allow me to raise an event in the derived class? I don't want
to put any behaviour in my abstract class, I want to put just an
interface!

It is an annoying thing. The CLR does allow for a protected raise_
method(atleast, by a vauge reading), however C# doesn't appear to support
this currently(MC++ is the only one I know that does.
Anyway, if you don't want any serious implementation in your base class, you
can do

public abstract class BaseClass
{
public event MyEventHandler MyEvent
{
add
{
Delegate.Combine(myEventDelegate,value);
}
remove
{
Delegate.Remove(myEventDelegate,value);
}
}
//this will contain the invocation list, might wanna convert this
//to a protected property so that you don't *have* to back off
myEventDelegate.
protected MyEventHandler myEventDelegate;
}
(note the syntax may be a touch off, I'm working from memory).
I would not be adverse to allowing a
raise
{

}
style method to events, however convincing the C# team of such may be
substatial work.
 
J

Jay B. Harlow [MVP - Outlook]

In addition to Chris's comments. I had a discussion about this in Oct 2003
on vb.dotnet.technical (recently converted to a web interface, I don't have
the new URL) titled "Multicast delegate guru's"

Our conclusion was you DONT! As BeginInvoke does not work on MultiCast
delegates, if you have more then a single handler attached to your event
(underlying delegate really), you cannot call BeginInvoke.

Here is the text from that thread:
---x--- cut here ---x---
By virtue of the error you are getting, the BeginInvoke method requires a
single method to use.

Which makes sense to me as when you are using BeginInvoke on your multicast
delegate are you expecting each routine to be on its own thread, or are you
expecting all routines to be on a single thread?

If you are expecting the first, I would not bother combining them I would
just BeginInvoke on each method (delegate). Alternatively you could use
Delegate.GetInvocationList and BeginInvoke on each method.

For the second I would have two delegates. The mutlicast delegate that has
the list of methods to execute, plus a singlecast delegate that is the
asynchronous method. This second routine would accept the multicast delegate
as a parameter, it would simply invoke the parameter.

Something like:

Delegate Sub Work(ByVal a As Integer, ByVal b As Integer)

Delegate Sub Worker(ByVal work As Work, ByVal a As Integer, ByVal b As
Integer)

Private Sub DoSub1(ByVal a As Integer, ByVal b As Integer)
' sub 1 process
End Sub

Private Sub DoSub2(ByVal a As Integer, ByVal b As Integer)
' sub 2 process
End Sub

' the asynchronous method
Private Sub DoWork(ByVal work As Work, ByVal a As Integer, ByVal b As
Integer)
work.Invoke(a, b)
End Sub

Dim sub1 As Work = AddressOf DoSub1
Dim sub2 As Work = AddressOf DoSub2
Dim work As Work = DirectCast([Delegate].Combine(sub1, sub2), Work)

Dim worker As Worker = AddressOf DoWork

worker.BeginInvoke(work, 1, 2, Nothing, Nothing)

---x--- cut here ---x---

Hope this helps
Jay
 
J

Jay B. Harlow [MVP - Outlook]

Chris,
Are you sure?

The link you gave is "Method Naming Guidelines".

Where as the two links I gave give Event Naming Guidelines & Event Usage
Guidelines.

Seeing as Elder was asking about Events I gave the two links on Events that
describe the "Standard" you mentioned in your first reply to him. How does
the Method Naming Guidelines come into play? Granted one of the two may be
more appropriate, however I find both together pretty much cover events.

Thanks
Jay
 
C

Chris Taylor

Sorry,

Actually the link I intended to supply was the second one you gave, I just
copied from the Address bar in explorer not noticing that the link did not
match the page, my mistake. I should have looked at your second link more
closely.

Again I apologize.
 
J

Jay B. Harlow [MVP - Outlook]

Chris,
copied from the Address bar in explorer not noticing that the link did not
I hate when that happens!

I figured (was hoping) we were on the same "page" (so to say).

Thanks
Jay
 
D

Daniel Billingsley

It may be interesting to note the following from the VB.NET documentation:

"Derived classes - classes that inherit characteristics from a base class -
can handle events raised by their base class using the Handles MyBase
statement"

This seems directly contradicting what is otherwise given as the recommended
practice. The idea of a derived class setting itself up as a listener to
its base class seems like a kludge to me. I found this thread because I was
translating some VB code to C# and the author's use of Handles MyBase.event
didn't sit right with me.

I also found a third source in addition to the two Jay gave.
http://msdn.microsoft.com/library/d...en-us/cpguide/html/cpconhandlinguserinput.asp
 

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