Передайте возвращаемое значение обратно через EventHandler

Я пытаюсь написать в API, и мне нужно вызвать обработчик событий, когда я получаю данные из таблицы. Что-то вроде этого:

    public override bool Run(Company.API api)
    {
        SomeInfo _someInfo = new SomeInfo();

        if (_someInfo.Results == 1)
            return true;
        else
            return false;

        using (MyTable table = new MyTable(api))
        {
            table.WhenData += new EventHandler<DataEventArgs<Record>>(table_WhenData);
            table.WhenDead += new EventHandler<EventArgs>(table_WhenDead);
            table.Start();
        }

    public void table_WhenData(object sender, DataEventArgs<Record> e)
    {
        return true;
    }

Проблема, с которой я сталкиваюсь, заключается в том, что я не знаю, как передать возвращаемое значение обратно из table_WhenData в метод Run.

Я пробовал много способов (например, пытался передать _someInfo методу), но я просто не могу правильно понять синтаксис.

Любое предложение приветствуется.


person Leroy Jenkins    schedule 18.09.2009    source источник
comment
обработчик события должен быть вызван откуда-то. это не показано в вашем коде? Это единственное место, где вы можете проверить возврат от обработчика.   -  person simon    schedule 18.09.2009
comment
Всем спасибо. Поскольку это API, большая часть кода не имеет доступа или не может быть изменена. Я просто хотел проверить это, прежде чем отправить его обратно разработчикам. Спасибо.   -  person Leroy Jenkins    schedule 18.09.2009


Ответы (4)


Общий шаблон здесь заключается не в том, чтобы возвращать какие-либо данные из обработчика событий, а в том, чтобы добавлять свойства к вашему объекту аргумента события, чтобы потребитель события мог установить свойства, к которым затем может получить доступ вызывающий. Это очень распространено в коде обработки пользовательского интерфейса; вы повсюду видите концепцию события «Отмена».

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

public class MyEventArgs : EventArgs
{
   public bool Cancel{get;set;}
}

public bool fireEvent()
{
    MyEventArgs e=new MyEventArgs();

    //Don't forget a null check, assume this is an event
    FireEventHandler(this,e);

    return e.Cancel;
}

public HandleFireEvent(object sender, MyEventArgs e)
{
 e.Cancel=true;
}

Изменить

Мне нравится, как Джон Скит сформулировал это: сделать EventArgs изменяемым. То есть потребитель события может изменить состояние объекта EventArgs, позволяя инициатору события получить доступ к этим данным.

person JoshBerke    schedule 18.09.2009
comment
В случае, когда есть несколько обработчиков событий (некоторые устанавливают значение true, а другие устанавливают значение этого объекта как false, для примера кода, приведенного выше), как определяет код подписчика события? это как последний подписчик который сет побеждает - person bashahul; 11.06.2016
comment
Хороший вопрос, вы, вероятно, захотите использовать другую структуру данных в этом случае в зависимости от ваших бизнес-правил. - person JoshBerke; 13.06.2016
comment
@bashahul - да, вызывающий абонент видит совокупность того, что делают все обработчики, поэтому, если несколько обработчиков устанавливают значение, оно будет последним. Если вы ожидаете несколько обработчиков, вам нужно решить, какова цель проекта, и все обработчики будут следовать этому. Например, в упомянутом Джошем паттерне Cancel, как только один обработчик устанавливает Cancel, другие обработчики могут проверять это и ничего не делать. В некоторых других ситуациях каждый обработчик может добавить элемент в список ответов, чтобы вызывающий мог видеть их все. - person ToolmakerSteve; 22.06.2018

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

В объявлении события/издателе:

// the delegate
public delegate string ReturnStringEventHandler(object sender, EventArgs args);
// the event
public event ReturnStringEventHandler StringReturnEvent;
// raise the event
protected void OnStringReturnEvent(EventArgs e)
    {
        if (StringReturnEvent != null)  // make sure at least one subscriber
              // note the event is returning a string
              string myString = StringReturnEvent(this, e);
    }

В подписчике события:

// Subscribe to event, probably in class constructor / initializer method
StringReturnEvent += HandleStringReturnEvent;

// Handle event, return data
private string HandleStringReturnEvent(object sender, EventArgs e)
{
    return "a string to return";
}

.NET предоставляет пример этого в событии AssemblyResolve, которое использует делегат ResolveEventHandler для возврата данных, в данном случае ссылки на нужную сборку. Статья MSDN о событии AssemblyResolve

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

person AFischbein    schedule 07.09.2012
comment
Если вы используете подход с пользовательским делегатом, что произойдет, если у вас есть несколько обработчиков события, возвращающих разные значения? Какое возвращаемое значение получает генератор событий? - person Shavais; 06.12.2013
comment
Вы упомянули определенный недостаток этого подхода. Последний обработчик события, который будет зарегистрирован с событием, — это тот, значение которого будет возвращено. msdn.microsoft.com/en-us/library/ aa691375%28VS.71%29.aspx В событии AssemblyResolve, о котором я упоминал, это нормально, так как оно предназначено для использования только по одному, но вы правы - в общем, это было бы плохим выбор дизайна, если требуется несколько возвращаемых значений. - person AFischbein; 06.12.2013
comment
Как насчет этого подхода для получения каждого из возвращаемых значений: stackoverflow.com/a/1237017/1999165 - person Eric Roller; 13.09.2015
comment
Вы можете получить исключение, называемое встроенный оператор, не может быть объявлением или помеченным оператором, просто добавьте открывающие и закрывающие фигурные скобки, окружающие строку myString = StringReturnEvent(this, e);. - person SurenSaluka; 10.04.2018

Единственный способ сделать это — сделать один из аргументов (предпочтительно «args», а не отправителя) изменяемым. Если он еще не изменчив, у вас в основном есть проблемы - просто нет способа получить информацию.

(Хорошо, есть один способ - вы можете оставить сам аргумент события неизменным, но сделать один из его членов методом, который в конечном итоге вызовет делегата, зарегистрированного кодом, вызывающим событие в первую очередь. Но это ужасно...)

person Jon Skeet    schedule 18.09.2009
comment
У меня болит голова, пытаясь понять, как сделать EventArgs изменяемым! - person JoshBerke; 18.09.2009
comment
Как насчет использования закрытия, как описано в моем ответе? - person Igor ostrovsky; 18.09.2009
comment
Игорь: Это сработает, но обычно часть кода, которой нужен результат, не является частью кода, подписывающегося на событие. - person Jon Skeet; 18.09.2009

Простое решение - использовать замыкание:

public override bool Run() {
    SomeInfo someInfo = ...
    table.WhenData += (obj, args) => {
        someInfo.Return = something
    };
}
person Igor ostrovsky    schedule 18.09.2009
comment
проблема, которая может возникнуть у меня с этим решением, связана с проблемами параллелизма. Один поток вызывает событие, другой читает переменную someInfo до завершения записи. Для этого потребуется какая-то многопоточная защита. - person simon; 19.09.2009