2009-06-19 159 views
3

如何在类之间传递事件?C#冒泡/传递事件

我知道这听起来很荒谬(而且是),但过去的一段时间里我一直在为此付出沉重的代价。搜索没有出现类似的问题,所以我想我会提出这个问题。

以下是涉及到的对象:

WinForm -> Speaker -> Tweeter 
        -> Woofer 

[扬声器,高音,低音]所有声明一个“SpeakToMe”事件发送一个简单的字符串消息。的事件所使用的标准图案宣称:

public delegate void SpeakToMeHandler(object sender, SpeakToMeEventArgs e); 
public event SpeakToMeHandler SpeakToMe; 
protected virtual void OnSpeakToMe(string message) 
{ 
    if (SpeakToMe != null) SpeakToMe(this, new SpeakToMeEventArgs(DateTime.Now.ToString() + " - " + message)); 
} 

SpeakToMeEventArgs是一个简单的类由含有字符串属性(消息)的EventArgs &继承。

就其本身而言,每个事件都能正常工作。例如,我在表单中设置了一个按钮来创建,订阅和发布[扬声器,高音单元,低音单元]的事件。每个都可以正确回报。

问题是扬声器创建[Tweeter,Woofer]并订阅他们的事件。

我想要的是[Tweeter,Woofer]发射他们的事件,发言者消耗它并且发射它自己的事件。我想这应该是非常简单的:

void tweeter_SpeakToMe(object sender, SpeakToMeEventArgs e) 
{ 
    Console.Out.WriteLine("the tweeter is speaking: " + e.Message); 
    this.OnSpeakToMe("tweeter rockin' out [" + e.Message + "]"); 
} 

通过这个功能(喇叭)步进,Console.Out.WriteLine工作。继续浏览OnSpeakToMe,显示委托为空。

演讲者的SpeakToMe事件由表单订阅。我明白这应该防止事件的委托为空。

我敢肯定,这是一个容易的,我错过了什么?

顺便说一句,万一你好奇我为什么要找这个。 [扬声器,高音扬声器,低音扬声器]是我长时间数据处理操作的演示替身。表单同时运行其中几个并需要每个类的进度更新。

一如既往,任何和所有的帮助非常感谢!

更新:感谢所有的反馈大家。我非常感谢帮助!我拿起了一些好的提示(@David Basarab & @Brian)以及关于如何构造事物的一些不同想法。再次,非常感谢!

+0

你能提供的表单如何订阅议长对象的代码片段? – LBushkin 2009-06-19 14:48:44

回答

4

如果我理解你想要的基本意义。让高音扬声器和低音扬声器发出一个事件,扬声器也被认购,然后自己发射。

这是我的代码具有该输出

OUTPUT

OnSpeak消息= OnSpeakToMeHander原单消息:燃煤通过高音

OnSpeak消息= OnSpeakToMeHander原单消息:由低音单元发射

class Program 
{ 

    static void Main(string[] args) 
    { 
     Console.Clear(); 

     try 
     { 
      Speaker speaker = new Speaker(); 
      speaker.speakerEvent += new SpeakToMeHandler(Program.OnSpeak); 

      // Cause events to be fied 
      speaker.Tweeter.CauseEvent(); 
      speaker.Woofer.CauseEvent(); 

     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("Error: {0}", ex.Message); 
      Console.WriteLine("Stacktrace: {0}", ex.StackTrace); 
     } 
    } 

    public static void OnSpeak(object sendere, SpeakToMeEventArgs e) 
    { 
     Console.WriteLine("OnSpeak Message = {0}", e.Message); 
    } 

} 

public delegate void SpeakToMeHandler(object sender, SpeakToMeEventArgs e); 

public class SpeakToMeEventArgs : EventArgs 
{ 
    public string Message { get; set; } 
} 

public class Speaker 
{ 
    public event SpeakToMeHandler speakerEvent; 

    public Tweeter Tweeter { get; set; } 
    public Woofer Woofer { get; set; } 

    public void OnSpeakToMeHander(object sender, SpeakToMeEventArgs e) 
    { 
     if (this.speakerEvent != null) 
     { 
      SpeakToMeEventArgs args = new SpeakToMeEventArgs 
       { 
        Message = string.Format("OnSpeakToMeHander Orginal Message: {0}", e.Message) 
       }; 

      this.speakerEvent(this, args); 
     } 
    } 

    public Speaker() 
    { 
     this.Tweeter = new Tweeter(); 
     this.Woofer = new Woofer(); 

     Tweeter.tweeterEvent += new SpeakToMeHandler(this.OnSpeakToMeHander); 
     Woofer.wooferEvent += new SpeakToMeHandler(this.OnSpeakToMeHander); 
    } 
} 

public class Tweeter 
{ 
    public event SpeakToMeHandler tweeterEvent; 

    public void CauseEvent() 
    { 
     SpeakToMeEventArgs args = new SpeakToMeEventArgs() 
      { 
       Message = "Fired By Tweeter" 
      }; 

     if (this.tweeterEvent != null) 
     { 
      this.tweeterEvent(this, args); 
     } 
    } 
} 

public class Woofer 
{ 
    public event SpeakToMeHandler wooferEvent; 

    public void CauseEvent() 
    { 
     SpeakToMeEventArgs args = new SpeakToMeEventArgs() 
      { 
       Message = "Fired By Woofer" 
      }; 

     if (this.wooferEvent != null) 
     { 
      this.wooferEvent(this, args); 
     } 
    } 
} 
+0

感谢大卫的回答。你的结构与我所掌握的非常相似。主要区别在于提升事件的统一功能。 仍然无法在我的错误发生的地方拼错100%,但是你的结构像魅力一样起作用。 我一定忽略了其中的一件...这是那些日子之一。 再次感谢! – Mark 2009-06-19 18:44:39

2

委托将为空的唯一方法是如果表单实际上未订阅该事件。您是否在某个时候创建​​了一个新的演讲者实例?如果您订购了一个实例,然后使用相同的变量创建了一个新实例,则新实例的事件将不会连接。

0

而不是传递事件,为什么不为事件分配额外的处理程序?你不仅限于一种方法。

//Handler class 
public class Speaker { 
    public delegate void HandleMessage(string message); 
    public event HandleMessage OnMessage; 
    public void SendMessage(string message) { 
     if (OnMessage != null) { OnMessage(message); } 
    } 
} 

//then used like... 
Speaker handler = new Speaker(); 
handler.OnMessage += (message) => { Console.WriteLine("Woofer: {0}", message); }; 
handler.OnMessage += (message) => { Console.WriteLine("Tweeter: {0}", message); }; 
handler.SendMessage("Test Message"); 
+0

正如我所理解的问题,信号流是在相反的方向?扬声器正在收听高音扬声器和低音扬声器,表格正在收听扬声器。也许我误解了它,但... – 2009-06-19 15:39:51

+0

可能,但在我看来,你仍然可以组织你的代码,以便事件处理程序照顾自己,而不是将一个事件传递给另一个...不知道虽然:) – Hugoware 2009-06-19 16:30:47

+0

@HBoss谢谢为了洞察。绝对是一个有趣的方法。不幸的是,弗雷德里克说得没错,对于这种特殊情况,流量是相反的。 – Mark 2009-06-19 17:21:11

1

您可以使用长形式的事件声明将事件传递给内部对象。

public class Speaker 
{ 
    public Speaker() 
    { 
     this.MyTweeter = new Tweeter(); 
    } 

    public Tweeter MyTweeter { get; private set; } 

    public event SpeakToMeHandler SpeakToMe 
    { 
     add { MyTweeter.SpeakToMe += value; } 
     remove { MyTweeter.SpeakToMe -= value; } 
    } 
} 
4

Eric Lippert针对if (SpeakToMe != null)的警告代码。虽然它可能不是你的情况的问题(例如,如果你从来没有删除事件),你应该养成使用这个代替的习惯:

var tmp = SpeakToMe; 
if (tmp!=null) tmp(/*arguments*/); 

在C#6和更高,认为这更简洁的代码来代替:

SpeakToMe?.Invoke(e) 

后一种方法,建议on the MSDN