2009-07-01 9 views
5

Diseño Pregunta - Evento polimórfica ManejoDiseño Ayuda - Evento polimórfica Manejo

Actualmente estoy tratando de reducir el número de controladores de evento en mi proyecto actual. Tenemos múltiples sistemas que envían datos a través de USB. Actualmente tengo una rutina para leer en los mensajes y analizar los detalles del encabezado inicial para determinar de qué sistema proviene el mensaje. Los encabezados son un poco diferentes, por lo que los EventArgs que creé no son lo mismo. Luego notifico a todos los "observadores" del cambio. Así que lo que tengo en este momento es la siguiente:

public enum Sub1Enums : byte 
{ 
    ID1 = 0x01, 
    ID2 = 0x02 
} 

public enum Sub2Enums : ushort 
{ 
    ID1 = 0xFFFE, 
    ID2 = 0xFFFF 
} 

public class MyEvent1Args 
{ 
    public Sub1Enums MessageID; 
    public byte[] Data; 
    public MyEvent1Args(Sub1Enums sub1Enum, byte[] data) 
    { 
     MessageID = sub1Enum; 
     Data = data; 
    } 
} 

public class MyEvent2Args 
{ 
    public Sub2Enums MessageID; 
    public byte[] Data; 
    public MyEvent2Args(Sub2Enums sub2Enum, byte[] data) 
    { 
     MessageID = sub2Enum; 
     Data = data; 
    } 
} 

código de Form1

public class Form1 
{ 
    public delegate void TestHandlerCurrentlyDoing(MyEvent1Args eventArgs1); 
    public delegate void TestHandlerCurrentlyDoingAlso(MyEvent2Args eventArgs2); 

    public event TestHandlerCurrentlyDoing mEventArgs1; 
    public event TestHandlerCurrentlyDoingAlso mEventArgs2; 

    public Form1() 
    { 
     mEventArgs1 += new TestHandlerCurrentlyDoing(Form1_mEventArgs1); 
     mEventArgs2 += new TestHandlerCurrentlyDoingAlso(Form1_mEventArgs2); 
    } 

    void Form1_mEventArgs2(MyEvent2Args eventArgs2) 
    { 
     // Do stuff here 
     Sub2Enums mid = my_event2_args.MessageID; 
     byte[] data = my_event2_args.Data; 
    } 

    void Form1_mEventArgs1(MyEvent1Args eventArgs1) 
    { 
     // Do stuff here 
     Sub1Enums mid = my_event1_args.MessageID; 
     byte[] data = my_event1_args.Data; 
    } 

Y en el algoritmo de análisis sintáctico tengo algo como esto en base al cual el mensaje es:

void ParseStuff() 
{ 
    if (mEventArgs1 != null) 
    { 
     mEventArgs1(new MyEvent1Args(Sub1Enums.ID1, new byte[] { 0x01 })); 
    } 
    if (mEventArgs2 != null) 
    { 
     mEventArgs2(new MyEvent2Args(Sub2Enums.ID2, new byte[] { 0x02 })); 
    } 
} 

Lo que realmente quiero hacer es esto:

public class Form1 
{ 
    public delegate void TestHandlerDesired(MyEvent1Args eventArgs1); 
    public delegate void TestHandlerDesired(MyEvent2Args eventArgs2); 

    public event TestHandlerDesired mEventArgs; 

    public Form1() 
    { 
     mEventArgs += new TestHandlerDesired (Form1_mEventArgs1); 
     mEventArgs += new TestHandlerDesired (Form1_mEventArgs2); 
    } 
} 

Y por razones de ambigüedad no podemos hacer esto. Entonces mi pregunta es ¿cuál sería un mejor enfoque para este problema?

+0

Parece una sintaxis de C#. Si es así, considere agregar este detalle a su pregunta. Además, creo que puedes volver a etiquetarlo para tener C# como etiqueta, y entonces tendrás más vistas. –

Respuesta

1

que podría hacer MyEvent1Args y MyEvent2Args derivar de una clase base común y hacer lo siguiente:

public class BaseEventArgs : EventArgs 
{ 
    public byte[] Data; 
} 

public class MyEvent1Args : BaseEventArgs 
{ … } 
public class MyEvent2Args : BaseEventArgs 
{ … } 


public delegate void TestHandlerWithInheritance(BaseEventArgs baseEventArgs); 

public event TestHandlerWithInheritance mTestHandler; 

mTestHandler += new TestHandlerWithInheritance(TestHandlerForEvent1Args); 
mTestHandler += new TestHandlerWithInheritance(TestHandlerForEvent2Args); 

    void TestHandlerForEvent1Args(BaseEventArgs baseEventArgs) 
    { 
     MyEvent1Args my_event1_args = (baseEventArgs as MyEvent1Args); 
     if (my_event1_args != null) 
     { 
      // Do stuff here 
      Sub1Enums mid = my_event1_args.MessageID; 
      byte[] data = my_event1_args.Data; 
     } 
    } 

    void TestHandlerForEvent2Args(BaseEventArgs baseEventArgs) 
    { 
     MyEvent2Args my_event2_args = (baseEventArgs as MyEvent2Args); 
     if (my_event2_args != null) 
     { 
      // Do stuff here 
      Sub2Enums mid = my_event2_args.MessageID; 
      byte[] data = my_event2_args.Data; 
     } 
    } 

Y en el algoritmo de análisis sintáctico tengo algo como esto en base al cual el mensaje es:

 if (mTestHandler!= null) 
     { 
      mTestHandler (new MyEvent1Args(Sub1Enums.ID1, new byte[] { 0x01 })); 
     } 
     if (mTestHandler!= null) 
     { 
      mTestHandler (new MyEvent2Args(Sub2Enums.ID2, new byte[] { 0x02 })); 
     } 
1

Tome un descanso del polimorfismo y estudie el uso de la indirección, específicamente el patrón del Agregador de eventos si aún no lo ha hecho; Fowler primero @http://martinfowler.com/eaaDev/EventAggregator.html y luego publicaciones de Jeremy Miller si necesita más ideas.

Saludos,
Berryl

1

usted podría considerar algunas opciones (no estoy seguro de qué es exactamente lo que quiere lograr aquí):

1. Crear una jerarquía de EventArgs y hacer que los observadores responsable de filtrar cosas que les interesan (esto es lo que propusiste en tu respuesta). Esto tiene sentido especialmente si algunos observadores están interesados ​​en múltiples tipos de mensajes, idealmente descritos por el tipo de clase base.

2. No utilice delegados de .Net, simplemente impleméntelo usted mismo de modo que cuando registre al delegado también tome el tipo de evento que espera. Esto supone que ha realizado el trabajo desde (1), pero desea pasar el filtrado a su clase y no a los observadores

P. ej. (no probado):

enum MessageType 
{ 
Type1,Type2 
} 
private Dictionary<MessageType, TestHandlerWithInheritance> handlers; 
public void RegisterObserver(MessageType type, TestHandlerWithInheritance handler) 
{ 
    if(!handlers.ContainsKey(type)) 
    { 
    handlers[key] = handler; 
    } 
    else 
    { 
    handlers[key] = Delegate.Combine(handlers[key] , handler); 
    } 
} 

Y cuando llega un mensaje nuevo, ejecuta el delegado correcto desde el diccionario de controladores.

3. Implemente los eventos en la forma en que se hace en WinForms, para que no tenga un evento subyacente para el evento siempre expuesto. Esto tiene sentido si espera tener más eventos que observadores.

ej .:

public event EventHandler SthEvent 
{ 
    add 
    { 
     base.Events.AddHandler(EVENT_STH, value); 
    } 
    remove 
    { 
     base.Events.RemoveHandler(EVENT_STH, value); 
    } 
} 

public void AddHandler(object key, Delegate value) 
{ 
    ListEntry entry = this.Find(key); 
    if (entry != null) 
    { 
     entry.handler = Delegate.Combine(entry.handler, value); 
    } 
    else 
    { 
     this.head = new ListEntry(key, value, this.head); 
    } 
} 


public void RemoveHandler(object key, Delegate value) 
{ 
    ListEntry entry = this.Find(key); 
    if (entry != null) 
    { 
     entry.handler = Delegate.Remove(entry.handler, value); 
    } 
} 


private ListEntry Find(object key) 
{ 
    ListEntry head = this.head; 
    while (head != null) 
    { 
     if (head.key == key) 
     { 
      return head; 
     } 
     head = head.next; 
    } 
    return head; 
} 

private sealed class ListEntry 
{ 
    // Fields 
    internal Delegate handler; 
    internal object key; 
    internal EventHandlerList.ListEntry next; 

    // Methods 
    public ListEntry(object key, Delegate handler, EventHandlerList.ListEntry next) 
    { 
     this.next = next; 
     this.key = key; 
     this.handler = handler; 
    } 
} 

Por favor, hágamelo saber si usted quiere que yo expando en cualquiera de las respuestas.

0

Si está tratando de reducir el número de identificadores de eventos para guardar la RAM, haga lo que microsoft do (en System.ComponentModel.Component) y use EventHandlerList para rastrear todos sus eventos. Here is an article that describes conserving memory use with an EventHandlerList y here is a similar article that's written in C#..

El quid de la cuestión es que se puede declarar una sola EventHandlerList (recuerde que debe deshacerse de él) en su clase, junto con una clave única:

public class Foo 
{ 
    protected EventHandlerList listEventDelegates = new EventHandlerList(); 
    static readonly object mouseDownEventKey = new object(); 

... anular la propiedad de evento:

public event MouseEventHandler MouseDown { 
    add { listEventDelegates.AddHandler(mouseDownEventKey, value); } 
    remove { listEventDelegates.RemoveHandler(mouseDownEventKey, value); } 
} 

... y proporcionar un método RaiseEvent:

protected void RaiseMouseDownEvent(MouseEventArgs e) 
{ 
    MouseEventHandler handler = (MouseEventHandler) base.Events[mouseDownEventKey]; 
    if (handler != null) 
    { 
     handler(this, e); 
    } 
} 

Por supuesto, que acaba de volver a utilizar el sam e EventHandlerList para todos sus eventos (pero con diferentes claves).

3

Si está intentando reducir el número de identificadores de eventos para simplificar/simplificar la codificación que tiene que hacer, aplicar el patrón de diseño Double Dispatch a los argumentos de su evento sería perfecto. Básicamente es una solución elegante (pero prolija) para tener que realizar conversiones de tipos seguros (/ es ​​una comprobación de instancia)

+2

Ok, algo así como este artical donde se implementa utilizando el patrón de visitante http://www.ddj.com/cpp/184403497?pgno=3, esto parece un gran enfoque – SwDevMan81

+0

sí, el patrón de visitante es probablemente el más grande usuario de doble despacho. Es un poco más fácil en C#. Este artículo comienza con una versión tradicional (tiempo de compilación, sin reflexión) y termina con una versión reflectante: http://accu.org/index.php/journals/1376 y aquí hay otra que hace uso de la reflexión: http:/www.stackoverflow.com/questions/42587/ –

+0

Implementé esto y lo publiqué bajo la pregunta [Patrón de diseño para manejar múltiples tipos de mensajes] (http://stackoverflow.com/a/16407143/95573) como respuesta, gracias para la sugerencia @Rob Fonseca-Ensor – SwDevMan81

Cuestiones relacionadas