C#冒泡/传递事件

时间:2009-06-19 14:42:47

标签: c# events messages

如何在课程之间传递事件?

我知道这听起来很荒谬(而且确实如此)但过去一段时间我一直对此感到难过。搜索没有出现类似的问题所以我想我会构成它。

以下是涉及的对象:

WinForm -> Speaker -> Tweeter
                   -> Woofer

[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&包含字符串属性(Message)。

他们自己,每个事件都很好。例如,我在表单中设置了一个按钮来创建,订阅和激活[Speaker,Tweeter,Woofer]的事件。每个都报告正确。

问题是当Speaker创建[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活动由表格订阅。我明白这应该可以防止事件的委托为空。

我确定这很简单,我错过了什么?

顺便说一下,如果你对我为什么要这个感到好奇。 [演讲者,Tweeter,Woofer]是我进行长时间数据处理操作的演示站点。表单同时运行其中的几个,并且需要每个类的进度更新。

与往常一样,非常感谢任何和所有帮助!

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

5 个答案:

答案 0 :(得分:4)

如果我从基本意义上理解你想要的东西。是要让Tweeter和Woofer发起扬声器订阅的事件,然后点燃它自己。

这是我的代码,具有此输出

<强>输出

OnSpeak消息= OnSpeakToMeHander原始消息:由Tweeter解雇

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);
        }
    }
}

答案 1 :(得分:4)

Eric Lippert警告if (SpeakToMe != null)代码。虽然在你的情况下它可能不是一个问题(即如果你永远不会删除事件),你应该养成使用它的习惯:

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

在C#6及更高版本中,请考虑使用此terser代码:

SpeakToMe?.Invoke(e)

建议采用后一种方法on the MSDN

答案 2 :(得分:2)

委托将为null的唯一方法是表单实际上没有订阅该事件。你有没有在某个时候创建​​一个新的扬声器实例?如果您订阅了一个实例,然后使用相同的变量创建了一个新实例,则不会连接新实例的事件。

答案 3 :(得分: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 :(得分: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");