C# 冒泡/传递事件

问题描述 投票:0回答:5

如何在班级之间传递事件?

我知道这听起来很荒谬(确实如此),但在过去的一段时间里我一直被这个问题难住了。搜索没有出现类似的问题,所以我想我会提出它。

涉及的对象如下:

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] 并订阅了它们的事件。

我想要的是 [Tweeter, Woofer] 触发他们的事件,Speaker 消耗它并触发它自己的事件。我认为这应该非常简单:

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,显示委托为空。

Speaker的SpeakToMe事件由表单订阅。我知道这应该防止事件的委托为空。

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

顺便说一句,如果你好奇我为什么要找这个。 [扬声器、高音扬声器、低音扬声器] 是我的演示替身,用于非常长的数据处理操作。该表格同时运行其中几个,并需要每个班级的进度更新。

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

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

c# events message
5个回答
4
投票

如果我从基本意义上理解你想要什么。是让高音扬声器和低音扬声器触发扬声器也订阅的事件,然后触发自己的事件。

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

输出

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

4
投票

Eric Lippert 警告不要使用

if (SpeakToMe != null)
代码。虽然在您的情况下这可能不是问题(即,如果您从不删除事件),但您应该养成使用它的习惯:

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

在 C#6 及更高版本中,请考虑以下简洁的代码:

SpeakToMe?.Invoke(e)

MSDN 上建议使用后一种方法


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


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

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");
    
© www.soinside.com 2019 - 2024. All rights reserved.