3

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

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

以下是涉及的对象:

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 并包含一个字符串属性(消息)。

就其本身而言,这些事件中的每一个都可以正常工作。例如,我在表单中设置了一个按钮来创建、订阅和触发 [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 事件由表单订阅。我知道这应该防止事件的委托为空。

我确定这很容易,我错过了什么?

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

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

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

4

5 回答 5

4

如果我从基本意义上理解你想要什么。是让 Tweeter 和 Woofer 触发一个 Speaker 也订阅的事件,然后触发它自己的事件。

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

输出

OnSpeak 消息 = OnSpeakToMeHander 原始消息:由 Tweeter 发射

OnSpeak Message = OnSpeakToMeHander 原始消息:由 Woofer 触发

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);
        }
    }
}
于 2009-06-19T15:02:48.640 回答
4

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

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

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

SpeakToMe?.Invoke(e)

MSDN上建议使用后一种方法

于 2009-06-19T15:25:09.530 回答
2

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

于 2009-06-19T15:00:48.253 回答
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; }
    }
}
于 2009-06-19T15:16:25.773 回答
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");
于 2009-06-19T15:03:32.880 回答