Pase un valor de retorno a través de un EventHandler
-
22-07-2019 - |
Pregunta
Estoy tratando de escribir en una API y necesito llamar a un controlador de eventos cuando obtengo datos de una tabla. Algo como esto:
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;
}
El problema que tengo es que no sé cómo pasar un valor de retorno de table_WhenData al método Run.
He intentado de muchas maneras (como intentar pasar _someInfo al método) pero parece que no puedo entender la sintaxis correcta.
Cualquier sugerencia es muy apreciada.
Solución
El patrón común aquí no es devolver ningún dato del controlador de eventos, sino agregar propiedades a su objeto de argumento de evento para que el consumidor del evento pueda establecer las propiedades a las que puede acceder el llamador. Esto es muy común en el código de manejo de la interfaz de usuario; ves el concepto de evento Cancelar por todas partes.
Lo siguiente es un pseudocódigo y no está listo para compilar. Su intención es mostrar el patrón.
public 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;
}
Editar
Me gusta cómo Jon Skeet redactó esto: hacer que el EventArgs
sea invaluable. Es decir, el consumidor del evento puede modificar el estado del objeto EventArgs
, lo que permite que el generador del evento llegue a esos datos.
Otros consejos
Sé que esta es una publicación antigua, pero en caso de que alguien la encuentre, ciertamente es posible hacer esto. Usted declara su propio delegado que devuelve un valor, luego basa el evento en este nuevo delegado. Aquí hay un ejemplo:
En el declarante / editor del evento:
// 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);
}
En el evento suscriptor:
// 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 proporciona un ejemplo de esto en el evento AssemblyResolve, que utiliza el delegado ResolveEventHandler para devolver datos, en este caso una referencia al ensamblaje deseado. Artículo de MSDN sobre el evento AssemblyResolve
Personalmente he usado tanto el evento AssemblyResolve como la técnica de delegado personalizado para devolver datos de un evento, y ambos funcionan como se esperaba en Visual Studio 2010.
La única forma de hacerlo es hacer que uno de los argumentos (preferiblemente los "argumentos" en lugar del remitente) sea mutable. Si aún no es mutable, básicamente tiene problemas, simplemente no hay forma de obtener la información.
(Bien, hay una forma: puede mantener el argumento del evento en sí mismo inmutable, pero hacer que un miembro sea un método que termine llamando a un delegado registrado por el código que genera el evento en primer lugar. Pero eso es horrible ... .)
Una solución simple es usar un cierre:
public override bool Run() {
SomeInfo someInfo = ...
table.WhenData += (obj, args) => {
someInfo.Return = something
};
}