C # пузыриться / проходя мимо события - PullRequest
3 голосов
/ 19 июня 2009

Как передать событие между классами?

Я знаю, что это звучит смешно (и это так), но я был озадачен этим в последнее время. Поиск не нашел похожего вопроса, поэтому я решил, что задам его.

Здесь задействованы следующие объекты:

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] и подписывается на их события.

Я хочу, чтобы [Твиттер, Низкочастотный громкоговоритель] запустил свое событие, Спикер поглотил его и запустил свое собственное событие. Я думал, что это должно быть очень просто:

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

Пройдя через эту функцию (в Speaker), Console.Out.WriteLine работает. Продолжая шагать по OnSpeakToMe, показывает, что делегат имеет значение null.

SpeakerToMe на событие докладчика подписывается формой. Я понял, что это должно предотвратить нулевой делегат события.

Я уверен, что это легко, что мне не хватает?

Кстати, если вам интересно, почему я ищу это. [Speaker, Tweeter, Woofer] - мои демо-стенды для действительно длительной операции обработки данных. Форма запускает несколько из них одновременно и требует обновления прогресса от каждого класса.

Как всегда, любая помощь очень ценится!

Обновление: Спасибо всем за отзывы. Я очень ценю помощь! Я подобрал пару хороших советов (@David Basarab & @Brian) и несколько разных идей о том, как структурировать вещи. Опять же, высоко ценится!

Ответы [ 5 ]

4 голосов
/ 19 июня 2009

Эрик Липперт предупреждает против if (SpeakToMe != null) кода. Хотя это может и не быть проблемой в вашем случае (то есть, если вы никогда не удаляете события), вы должны привыкнуть использовать вместо этого:

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

В C # 6 и выше рассмотрите этот код более кратко:

SpeakToMe?.Invoke(e)

Последний подход предлагается на MSDN

4 голосов
/ 19 июня 2009

Если бы я понял, что вы хотели в основном смысле. Должен ли твитер и низкочастотный динамик инициировать событие, на которое подписчик тоже подписан, а затем запустить свое собственное.

Вот мой код с таким выводом

OUTPUT

Сообщение 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);
        }
    }
}
2 голосов
/ 19 июня 2009

Единственный способ, которым делегат будет нулевым, - это если форма на самом деле НЕ подписана на событие. Вы когда-нибудь создавали новый экземпляр динамика? Если вы подписали один экземпляр, а затем создали новый экземпляр, используя ту же переменную, события для нового экземпляра не будут подключены.

1 голос
/ 19 июня 2009

Вы можете использовать длинную форму объявления события для передачи события внутреннему объекту.

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 голосов
/ 19 июня 2009

Вместо передачи события, почему бы не назначить дополнительный обработчик для события? Вы не ограничены только одним методом.

//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");
...