2010-08-02 5 views
0

J'ai eu du mal à écrire un titre plus approprié à cette question - je vais essayer de le nettoyer quand je comprendrai mieux la solution et la bonne terminologie. : DComment agréger des événements à travers une collection d'objets identiques (même classe - différentes instances) à travers un autre 'objet d'agrégation'?

Il y a un motif qui se répète dans les deux derniers projets sur lesquels j'ai travaillé (les projets sont tous deux fortement pilotés par les événements).

Le modèle est:

public interface ISourceOfEvents { 
    event Action<EventInfo1> SomethingHappened1; 
    event Action<EventInfo2> SomethingHappened2; 
    {...} 
} 

public class SingleSourceOfEvents : ISourceOfEvents { 
    public event Action<EventInfo1> SomethingHappened1 = delegate { }; 
    public event Action<EventInfo2> SomethingHappened2 = delegate { }; 
    {...} 
} 

public class GroupSourceOfEvents : ISourceOfEvents { 

    public IList<ISourceOfEvents> SourceOfEventsGroup { get; private set; } 

    public event Action<EventInfo1> SomethingHappened1 = delegate { }; 
    public event Action<EventInfo2> SomethingHappened2 = delegate { }; 
    {...} 

    public GroupSourceOfEvents(IList<ISourceOfEvents> sourceOfEventsGroup) { 

     SourceOfEventsGroup = sourceOfEventsGroup; 

     foreach (var s in SourceOfEventsGroup) { 

      s.SomethingHappened1 += x => SomethingHappened1(x); 
      s.SomethingHappened2 += x => SomethingHappened2(x); 
      {...} 
     } 
    } 
} 

Ma question se résume à « comment puis-je faire pour le constructeur GroupSourceOfEvents carte les liaisons d'événements automatiquement? ».

Actuellement, le code est difficile à maintenir car il existe plusieurs emplacements dans les projets pour lesquels de nouveaux événements doivent être ajoutés. L'interface ISourceOfEvents me permet d'ajouter l'événement à un emplacement central et d'obtenir des erreurs de compilation partout où je dois ajouter l'événement mais j'ai besoin d'une solution similaire pour les mappages de classeurs d'événements dans le GroupSourceOfEvents - ou pour qu'il soit complètement dynamique.

Je voudrais quelque chose de similaire à ceci:

// PSEUDO-CODE WARNING! :D 
// PSEUDO-CODE WARNING! :D 
// PSEUDO-CODE WARNING! :D 

public GroupSourceOfEvents(IList<ISourceOfEvents> sourceOfEventsGroup) { 

    SourceOfEventsGroup = sourceOfEventsGroup; 

    var events = typeof(ISourceOfEvents).GetEvents(); 

    foreach (var s in SourceOfEventsGroup) { 

     foreach (var e in events) { 
      e.AddEventHandler(s, e.GetRaiseMethod(this)); 
     } 
    } 
} 

La moitié de mon problème est la cartographie des paramètres ensemble. Dans le code qui fonctionne actuellement (le top sample), une expression lambda est utilisée mais je ne suis pas sûr de savoir comment cela peut être réalisé en utilisant le 'style' de la technique que j'utilise dans mon pseudo code (l'échantillon du bas). L'autre moitié de mon problème est que GetRaiseMethod(this) renvoie MethodInfo par opposition à delegate (ce qui est ce que je devrais utiliser AddEventHandler) et je ne sais pas comment "obtenir" le delegate.

Toute aide serait grandement appréciée. Je suis également ouvert à différents modèles ou approches qui peuvent être plus standard (si ce n'est pas le cas).

J'utilise C# et .Net 4.

Répondre

1

Je ne sais pas si je comprends tout à fait ce que vous essayez de faire; d'après ce que je comprends, vous essayez de créer un objet "agrégateur" (GroupSourceOfEvents) qui contient une liste d'objets ISourceOfEvents.

Ensuite, vous souhaitez autoriser les abonnés à s'abonner aux événements exposés par l'objet GroupSourceOfEvents. Ces événements sont déclenchés chaque fois que l'un des objets ISourceOfEvents contenus déclenche leurs événements.

Voici deux solutions qui, selon moi, pourraient répondre à vos besoins. Ces solutions utilisent des accesseurs d'événement (ajouter/supprimer). Le premier est un exemple simple avec un certain nombre d'embûches. Le second est un exemple plus complexe qui, je pense, aborde tous les pièges du premier exemple.

public class GroupSourceOfEvents : ISourceOfEvents 
{ 
    public IList<ISourceOfEvents> SourceOfEventsGroup { get; private set; } 

    public event Action<EventInfo1> SomethingHappened1 
    { 
     add 
     { 
      foreach (var s in SourceOfEventsGroup) 
      { 
       s.SomethingHappened1 += value; 
      } 
     } 
     remove 
     { 
      foreach (var s in SourceOfEventsGroup) 
      { 
       s.SomethingHappened1 -= value; 
      } 
     } 
    } 

    public event Action<EventInfo2> SomethingHappened2 
    { 
     add 
     { 
      foreach (var s in SourceOfEventsGroup) 
      { 
       s.SomethingHappened2 += value; 
      } 
     } 
     remove 
     { 
      foreach (var s in SourceOfEventsGroup) 
      { 
       s.SomethingHappened2 -= value; 
      } 
     } 
    } 
    // {...} 

    public GroupSourceOfEvents(IList<ISourceOfEvents> sourceOfEventsGroup) 
    { 
     SourceOfEventsGroup = sourceOfEventsGroup; 

     foreach (var s in SourceOfEventsGroup) 
     { 
      // {...} 
     } 
    } 
} 

Le principal écueil que je peux penser est que vous devez veiller à ce que ont été ajoutés tous vos objets ISourceOfEvents à votre objet GroupSourceOfEvents avant que les abonnés commencent à souscrire à l'un des événements. En effet, si vous ajoutez l'un des objets ISourceOfEvents de la liste, aucun des écouteurs précédemment abonnés ne sera averti des événements sur les objets nouvellement ajoutés. De même, si un abonné se désabonne à un événement sur GroupSourceOfEvents après que l'un de ces objets ISourceOfEvents a été supprimé de la liste, les événements sur ces objets supprimés ne seront pas correctement désinscrits.Un autre problème est qu'il n'y a pas de verrouillage dans les accesseurs d'ajout/suppression des événements, ce qui pourrait être un problème si le multithreading est impliqué.

Vous pourriez aborder ces pièges avec une version plus compliquée de la même approche; vous pouvez avoir une variable de délégué privée qui correspond à chaque événement, et dans le code d'ajout/suppression pour chaque événement, vous pouvez utiliser la variable locale pour stocker tous les abonnés. Ensuite, vous devez éviter d'exposer votre liste SourceOfEventsGroup publiquement et fournir des méthodes pour ajouter/supprimer de la liste, de sorte qu'en ajoutant, vous pouvez utiliser vos variables de délégué privé pour vous abonner aux événements du nouvel objet, ou en supprimant vous pouvez utiliser les variables de délégué privé pour se désabonner aux événements de l'objet supprimé. Voici une autre version de l'exemple ci-dessus qui tente de résoudre les problèmes que j'ai mentionnés. Notez que j'ai exposé la liste comme IEnumerable<ISourceOfEvents> pour éviter d'exposer publiquement la fonctionnalité d'ajout/suppression de la liste. J'ai ajouté la méthode AddSource afin de permettre l'ajout d'une source et de m'abonner aux événements de cette source en utilisant les abonnés qui ont déjà été enregistrés. De même, il existe une méthode RemoveSource pour gérer la suppression d'une source de la liste et la désinscription de ses événements. Il existe également des verrous pour résoudre les problèmes de multithreading potentiels.

public class GroupSourceOfEvents : ISourceOfEvents 
{ 
    private object SourceOfEventsGroupLock = new object(); 

    private IList<ISourceOfEvents> _SourceOfEventsGroup; 

    public IEnumerable<ISourceOfEvents> SourceOfEventsGroup 
    { 
     get { return _SourceOfEventsGroup; } 
    } 

    public void AddSource(ISourceOfEvents source) 
    { 
     lock (SourceOfEventsGroupLock) 
     { 
      source.SomethingHappened1 += _SomethingHappened1; 
      source.SomethingHappened2 += _SomethingHappened2; 
      _SourceOfEventsGroup.Add(source); 
     } 
    } 

    public void RemoveSource(ISourceOfEvents source) 
    { 
     lock (SourceOfEventsGroupLock) 
     { 
      source.SomethingHappened1 -= _SomethingHappened1; 
      source.SomethingHappened2 -= _SomethingHappened2; 
      _SourceOfEventsGroup.Remove(source); 
     } 
    } 

    private Action<EventInfo1> _SomethingHappened1; 
    public event Action<EventInfo1> SomethingHappened1 
    { 
     add 
     { 
      lock (SourceOfEventsGroupLock) 
      { 
       _SomethingHappened1 += value; 

       foreach (var s in SourceOfEventsGroup) 
       { 
        s.SomethingHappened1 += value; 
       } 
      } 
     } 
     remove 
     { 
      lock (SourceOfEventsGroupLock) 
      { 
       _SomethingHappened1 -= value; 

       foreach (var s in SourceOfEventsGroup) 
       { 
        s.SomethingHappened1 -= value; 
       } 
      } 
     } 
    } 

    private Action<EventInfo2> _SomethingHappened2; 
    public event Action<EventInfo2> SomethingHappened2 
    { 
     add 
     { 
      lock (SourceOfEventsGroupLock) 
      { 
       _SomethingHappened2 += value; 

       foreach (var s in SourceOfEventsGroup) 
       { 
        s.SomethingHappened2 += value; 
       } 
      } 
     } 
     remove 
     { 
      lock (SourceOfEventsGroupLock) 
      { 
       _SomethingHappened2 -= value; 

       foreach (var s in SourceOfEventsGroup) 
       { 
        s.SomethingHappened2 -= value; 
       } 
      } 
     } 
    } 
    // {...} 

    public GroupSourceOfEvents(IList<ISourceOfEvents> sourceOfEventsGroup) 
    { 
     _SourceOfEventsGroup = sourceOfEventsGroup; 
    } 
} 

EDIT: J'ai essayé de nettoyer ma réponse en réorganisant les paragraphes, des phrases reformulant, et la suppression d'une phrase inachevée. Le contenu reste le même.

Questions connexes