Events - Delegates - Inheritance

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

There are a set of clients who need to be notified of certain events. I have
used events and delegates (publisher-Subscriber model) for the

notification mechanism. All the clients register with the event publisher
and subscribe for the events that they are interested in. When a certain
event happens, the subscribers are notified about it.


I want the clients to return a value after their callback method is called.
If any of the client returns true, I do not want to invoke the other
subscribers. The order in which the clients are notified is immaterial.

Problem:
How can the clients return a value? According to the MSDN documentation, the
return value is void in the delegate signature. I can change it to int, but
FxCop complains about it.


In Applied Microsoft .Net Framework, Jeffrey Richter talks about using
inheritance to solve this problem - "The derived type might decide not

to have the event forwarded on." Can someone please shed some light on how
to solve the problem?
 
Hi,

I think the thing you talk about in the end applies to the case, when a
derived class decides not to call the parent's event handler (like in a
Click event in Windows forms), or something like that.

Multicast delegates always should return void (note the SHOULD in
there). This is because when you invoke them (or an event), and say you
have 10 event handlers registered, which return value would you use? For
this reason, multicast delegates should return void.

However, you should take a look at the GetInvocationList() method of the
MulticastDelegate class (which is what delegates are anyway). This
method will return an array of Delegate objects. You could then use this
array to invoke the delegates manually, and check for a return value, if
you want to continue. This would require only changes in the publisher,
the subscribers would subscribe to the events just as they do now. Of
course you should change the delegates to return a bool value.

When you have the Delegate[] array that is returned, you can go through
that one by one and call the DynamicInvoke() method on them, and check
the return value. Based on that, you can either continue invoking the
delegates or break the loop.

Hope this helps.

-Lenard


PS.
Here is a simple example (although I used static events and methods to
simplyfy):

class Program
{
static event CanContinueDelegate canContinue;

static void Main ( string[] args )
{
canContinue += new CanContinueDelegate ( canContinueEvent );
canContinue += new CanContinueDelegate ( canContinueEvent );
canContinue += new CanContinueDelegate ( canContinueEvent );

Delegate[] delegateList = canContinue.GetInvocationList ();
foreach ( Delegate d in delegateList )
{
if ( (bool)d.DynamicInvoke ( new object[] { "not" } )
== false )
{
break;
}
}

}

static bool canContinueEvent ( string s )
{
Console.WriteLine ( s );
if ( s == "not" )
{
return false;
}
else
{
return true;
}
}
}
 
Or just keep it simple :)

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace Listeners
{
public interface IListener
{
bool Notify();
}
public class Client : IListener
{
private bool status = false;
private int ID = 0;
public Client(int ID)
{
this.ID = ID;
}
public void TurnOn()
{
this.status = true;
}
public bool Notify()
{
Console.WriteLine("Client "+ID.ToString()+" Notified");
return status;
}
}
class Broadcaster
{
private ArrayList listeners = new ArrayList();
// ASSERT listener is not null
public void Register(IListener listener) {
if (listener == null) { throw new ArgumentException(); }
listeners.Add(listener);
}
public void Broadcast() {
foreach(IListener l in listeners) {
if (l.Notify()) { break; }
}
}
static void Main(string[] args)
{
Client c1 = new Client(1);
Client c2 = new Client(2);
Client c3 = new Client(3);
c2.TurnOn();
Broadcaster b = new Broadcaster();
b.Register(c1);
b.Register(c2);
b.Register(c3);
b.Broadcast();
Console.ReadLine();
}
}
}

Regards,
Jeff
 
You can do this with events (in their original sense - no hacked return
value) quite easily, by using a different EventArgs class - ideally one
derived from CancelEventArgs, but any custom EventArgs derivative would do,
as long as it has a (settable) property on the class to allow you to
indicate failure.

Fully working example follows; outputs from the first 2 subscribers, but not
the last.

Hope this helps,

Marc

========

public class EventTest {


public static void Main() {
EventTest test = new EventTest();
test.MyEvent += DoSomething;
test.MyEvent += DoSomethingAndComplain;
test.MyEvent += DoSomethingElse;
test.FireTheEvent();
}

static void DoSomething(object sender,
System.ComponentModel.CancelEventArgs e) {
Console.WriteLine("DoNothing");
}
static void DoSomethingAndComplain(object sender,
System.ComponentModel.CancelEventArgs e) {
Console.WriteLine("DoSomethingAndComplain");
e.Cancel = true;
}
static void DoSomethingElse(object sender,
System.ComponentModel.CancelEventArgs e) {
Console.WriteLine("DoSomethingElse");
}

public void FireTheEvent() { // public accessor just to trigger
things
OnMyEvent();
}

public event System.ComponentModel.CancelEventHandler MyEvent;

protected void OnMyEvent() {
if (MyEvent != null) {
System.ComponentModel.CancelEventArgs args = new
System.ComponentModel.CancelEventArgs();
foreach (System.ComponentModel.CancelEventHandler listener
in MyEvent.GetInvocationList()) {
listener(this, args);
if (args.Cancel)
break;
}
}
}

}
 

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

Back
Top