2011-08-16 1 views
2

Je mets "événement" entre guillemets parce que je me rends compte que c'est un peu de syntaxe de sucre, plutôt qu'un vrai type.Existe-t-il un moyen d'accomplir l'équivalent de passer un "événement" par référence?

J'ai quelques événements qui sont simplement enchaînés à des événements correspondants dans une autre classe. Ainsi, lorsque l'événement est déclenché, le passage est comme

Raiser -> Proxy -> Abonné

Ainsi, dans la classe Proxy J'ai un modèle commun comme celui-ci:

Raiser.SomeEvent += 
    (_, args) => 
    { 
     if (this.SomeEvent != null) 
      this.SomeEvent(this, args); 
    }; 

Pour ranger mon Code que je voulais faire avancer ce à une autre méthode qui retourne un nouveau délégué qui enveloppe le code appelant événement ci-dessus:

public static EventHandler GetHandlerDelegate(EventHandler handler, Object sender) 
{ 
    return 
     (_, args) => 
     {     
      if (handler != null) 
       handler(sender, args); 
     }; 
    }  

Et puis dans Proxy je peux faire:

Raiser.SomeEvent += GetHandlerDelegate(this.SomeEvent, this); 

Ce qui est beaucoup plus propre.

Eh bien c'est bien tant que l'abonné ne décide pas de s'abonner à Proxy.SomeEvent après l'appel ci-dessus. Malheureusement, je ne passe pas l'événement par référence comme je l'espérais; Je comprends maintenant que je ne fais que passer la liste d'invocation, donc quand OtherClass.SomeEvent arrive et que la méthode anonyme est appelée et invoque le "événement" (délégué) qui lui a été donné, seuls les délégués qui ont été ajoutés à cet événement à l'époque J'ai appelé GetHandlerDelegate() sera appelé. Bien que cela suffise réellement pour ma situation actuelle, il n'est vraiment pas acceptable de le coder de cette façon. J'ai lu d'autres questions SO et je pense qu'il y a quelque chose qui s'appelle Reactive Extensions qui pourrait aider, mais pour le moment je cherche une solution plus simple s'il y en a une. (Sinon, je ne le ferai pas.)

Existe-t-il un autre moyen d'accomplir ce que j'essaye de faire, sans le désavantage?


Si cette question ne sait pas, s'il vous plaît voir my answer qui aide, espérons-le clarifier.

Répondre

0

Depuis mon objectif initial de faire:

Raiser.SomeEvent += GetHandlerDelegate(this.SomeEvent, this); 

est impossible, je me suis compromis et trouver ceci:

Raiser.SomeEvent += (_, args) => RaiseEvent(this.SomeEvent, this, args); 

Alors que GetHandlerDelegate() retourneraient un délégué qui déclenche l'événement, RaiseEvent() simplement (vous l'avez deviné) soulève l'événement.

public static void RaiseEvent(EventHandler _event, Object sender, EventArgs args) 
{ 
    if (_event != null) 
     _event(sender, args); 
} 

Et pour soutenir les événements en utilisant EventArgs personnalisés:

public static void RaiseEvent<TArgs>(EventHandler<TArgs> _event, Object sender, TArgs args) 
    where TArgs : EventArgs 
{ 
    if (_event != null) 
     _event(sender, args); 
} 

J'ai mis ces méthodes dans une classe d'aide statique, l'appel réel est un peu plus laid; voici un exemple:

ViewControl.OpenFilesetClick += (_, args) => EventHelper.Raise(OpenFilesetClick, this, args); 

(je aussi rebaptisés la méthode pour élever() et laissé tomber le this en option à partir du nom de l'événement étant passé).

Mais je ne suis pas entièrement convaincu si cela vaut la peine, étant donné que l'alternative était sans doute plus facile à lire:

ViewControl.OpenFilesetClick += (_, args) => 
{ 
    if (OpenFilesetClick != null) 
     OpenFilesetClick(this, args); 
}; 

Quoi qu'il en soit, il est un moyen intéressant d'en apprendre davantage sur la façon dont les événements et les délégués travaillent (ou comment ils ne fonctionnent pas).

3

EDIT: D'accord, je pense que je comprends maintenant. C'est en fait assez simple. Vous devriez être en mesure d'écrire le proxy d'avoir juste un événement, puis faire le proxy s'abonnez-vous à l'événement Raiser, comme celui-ci (juste pour EventHandler - Je viendrai plus tard):

Proxy proxy = new Proxy(); 
raiser.SomeEvent += Proxy.Handler; 

// Then in the subscriber... 
proxy.ProxiedEvent += (whatever) 

// And the proxy class... 
public class Proxy 
{ 
    public event EventHandler ProxiedEvent; 

    public void Handler(object sender, EventArgs e) 
    { 
     EventHandler proxied = ProxiedEvent; 
     if (proxied != null) 
     { 
      // Or pass on the original sender if you want to 
      proxied(this, e); 
     } 
    } 
} 

Maintenant, la difficulté ici est de le faire fonctionner de manière générique. Je ne peux actuellement penser à aucun moyen de le faire, même si je suis un peu distrait en ce moment.

Est-ce le genre de choses auxquelles vous pensiez, ou est-ce que cela vous aide au moins à penser différemment?

+0

Jon, merci pour la réponse, mais en tant que simple mortel sous-Skeet, j'ai du mal à voir les détails implicites. En d'autres termes, je suis perdu. Pourriez-vous élaborer pelase? Comment cela peut-il être appliqué à mon problème? –

+0

@Charles: Il est possible que j'aie mal compris la question ... Je la trouve un peu difficile à suivre en ce moment, mais c'est aussi susceptible d'être de ma faute que de vous-même. Cependant, le EventWrapper ici est fondamentalement une façon d'abstraire le concept «quelque chose que vous pouvez vous abonner et vous désabonner». C'est ce qu'est un événement, fondamentalement. Vous ne serez pas en mesure d'obtenir l'événement lui-même en tant que délégué, car ce n'est pas * un * délégué. –

+0

J'ai l'air d'avoir mal exprimé mes questions, donc je suis sûr que je suis responsable de toute confusion, pas de vous! Essentiellement je voulais passer un délégué _dA_ à une méthode et récupérer un nouveau délégué _dB_ qui appelle _dA_, avec le kicker étant que si la liste d'invocation de _dA_ change alors, je veux que la liste d'invocation _updated_ soit invoquée quand j'appelle _dB_. (J'ai essayé ceci sans utiliser un événement du tout - juste un délégué - pour confirmer par moi-même que le problème concerne vraiment le fonctionnement de _delegates_). –

Questions connexes