1 событие класса издателя, несколько подписчиков, разные параметры

Я использую делегаты и события для передачи данных в своем приложении, но хочу, чтобы срабатывание 1 события передавало разные наборы данных в разные места.

Проблема

У меня есть основной класс, который выполняет некоторую работу. После завершения работы я хочу, чтобы он уведомил ряд других классов (включая пользовательский интерфейс), чтобы можно было выполнить другие действия. Информация, которая нужна другим классам, отличается.

Пример

Main.cs — выполняет действие и хочет обновить пользовательский интерфейс, отправить текстовое сообщение и записать в файл журнала.

Пользовательский интерфейс обновляет DataGrid, поэтому ему нужны отдельные поля.

Для записи файла журнала нужна вся строка целиком в виде массива/списка.

Для кода текстового сообщения требуются Line, LineNumber и FileName, но в виде строки с разделителями табуляции.

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

Я пробовал создавать разные расширения EventArgs, но если я попытаюсь объявить двух делегатов с разными подписями, я получу сообщение об ошибке.

Любая помощь приветствуется.

FrmMain.cs

Main main = new Main();
main.PatternFound += OnPatternFound;

main.DoSomeWork();

private void OnPatternFound(object source, LineEventArgs e)
    {
        UpdateDataGrid(e.Line, e.FileName, e.LineNumber);
    }

private void UpdateDataGrid(string line, string file, int lineNumber)
    {
        if (InvokeRequired)
        {
            Invoke(new Action<string, string, int>(UpdateDataGrid), line, file, lineNumber);
        }
        else
        {
            dgResults.Rows.Add(line, file, lineNumber);
        }            
    }

Main.cs

public delegate void PatternFoundEventHandler(object sender, LineEventArgs e);
public event PatternFoundEventHandler PatternFound;

protected virtual void OnPatternFound(string line, string fileName, int lineNumber)
    {
        PatternFound?.Invoke(this, new LineEventArgs { Line = line, FileName = fileName, LineNumber = lineNumber });
    }

public void DoSomeWork()
{
    //Finished my work
    OnPatternFound(line, file, lineNumber);
}

LineEventArgs.cs

public class LineEventArgs : EventArgs
{
    public string Line { get; set; }
    public string FileName { get; set; }
    public int LineNumber { get; set; }
}

person Ninja    schedule 09.10.2017    source источник
comment
информация, которая нужна другим классам, отличается — вам придется решить, что поместить в EventArgs, чтобы удовлетворить большинство потребностей. Или просто добавьте несколько событий. Лично мне нравится другой подход, где sender используется как способ доступа ко всем как к членам экземпляра (EventArgs не используется). Однако будьте осторожны с многопоточностью.   -  person Sinatr    schedule 09.10.2017
comment
Спасибо Синатр. Так что с тем, как вы это делаете, в подписчиках вы бы взяли источник и знали, что вызывающий абонент был Main. Оттуда вы получите доступ к Main.Line, Main.LineNumber, Main.FileName напрямую, поскольку они являются общедоступными свойствами?   -  person Ninja    schedule 09.10.2017
comment
Да. Хотя, как я уже сказал, будьте осторожны. InvokeRequired говорит, что у вас может быть многопоточность. Для доступа к общедоступным членам может потребоваться какой-либо шаблон приобретения. Например. что-то плохое может произойти, если вы прочитаете публичное свойство со значением previous события. Вот где EventArgs безопасны, как только экземпляр будет создан, он будет сохраняться (как правило, неизменным) для всех обработчиков событий для данного события. Следующее событие - новое EventArgs.   -  person Sinatr    schedule 09.10.2017


Ответы (1)


То, чего вы пытаетесь достичь, возможно, не является хорошим подходом по нескольким причинам. Вы решили придерживаться событий, что означает, что вы решили сделать то, что называется «инверсией управления».

Вы реализовали свой основной класс таким образом, который говорит: «Я выполняю какое-то сопоставление с образцом, и я расскажу всем, кому интересно, что я нашел, и я делаю это так, как определяет моя реализация event-args». В результате подписчики должны принять эту информацию как есть или оставить ее.

Строго говоря, то, что вы сказали...

Выполняет действие и хочет обновить пользовательский интерфейс, отправить текстовое сообщение и записать в файл журнала.

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

Например, вы можете прикрепить к этому событию три разных обработчика событий. Затем каждый обработчик должен будет преобразовать данные в нужный ему формат: обработчик регистрации должен будет преобразовать их в массив, обработчик пользовательского интерфейса должен будет объединить их и так далее.

Другая альтернатива заключается в том, что у вас есть один обработчик и отправка на три разных метода, например, метод OnPatternFound будет вызывать метод UpdateUI и метод Log и так далее.

Наконец, вы также можете избавиться от инверсии управления и перенести свою логику в основной класс, но я бы не рекомендовал этого! Ваш подход хорош, но вы просто не должны пытаться вызвать одно и то же событие тремя разными способами. Это решать подписчикам.

person Peit    schedule 09.10.2017
comment
Спасибо, Пит. Поэтому, если я попытаюсь сохранить код как можно более чистым, если бы я реализовал ваше предложение, другой альтернативой было бы то, что у вас есть один обработчик и отправка на три разных метода, чтобы избежать нарушения DIP, мне пришлось бы создать какой-то вид интерфейса и передать экземпляр LogWriter, TextMessageSender и т. д. конструктору основного класса? Если нет, есть ли другой способ добиться этого без жесткой привязки моего кода? - person Ninja; 10.10.2017
comment
Нет, ты неправильно меня понял. Конечно, у вас есть много разных возможностей. Я имел в виду именно то, что вы написали, за исключением того, что ваш метод OnPatternFound отправляет три значимых других метода: private void OnPatternFound(object source, LineEventArgs e) { UpdateDataGrid(e.Line, e.FileName, e.LineNumber); WriteLogMessage(...); SendTextMessage(...); }. - person Peit; 10.10.2017
comment
Понятно. Теперь я понимаю, что вы имеете в виду. Спасибо. - person Ninja; 10.10.2017