2010-04-23 18 views
43

In meiner .NET-Anwendung abonniere ich Ereignisse aus einer anderen Klasse. Das Abonnement ist bedingt. Ich abonniere Ereignisse, wenn das Steuerelement sichtbar ist, und entferne es, wenn es unsichtbar wird. Unter bestimmten Bedingungen möchte ich das Ereignis jedoch nicht abmelden, auch wenn das Steuerelement nicht sichtbar ist, da ich das Ergebnis einer Operation erhalten möchte, die in einem Hintergrundthread stattfindet.So ermitteln Sie, ob ein Ereignis bereits abonniert wurde

Gibt es einen Weg, durch den ich feststellen kann, ob eine Klasse dieses Ereignis bereits abonniert hat?

Ich weiß, wir können es in der Klasse tun, die dieses Ereignis auslösen wird, indem Sie das Ereignis für null überprüfen, aber ich wie mache ich es in einer Klasse, die dieses Ereignis abonnieren wird?

+2

prüfen Link http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/d7d8791f-6aef-4fda-ae0e-5eddcb856706/ –

+0

wenn es nur etwa ob jemand * * ist abonniert:'bool subscribeTo = theEvent! = null' – Mark

Antwort

51

Das event Schlüsselwort wurde explizit erfunden, um zu verhindern, dass Sie tun, was Sie tun möchten. Es beschränkt den Zugriff auf das zugrunde liegende delegate-Objekt, so dass niemand direkt mit den von ihm gespeicherten Ereignis-Handler-Subskriptionen umgehen kann. Ereignisse sind Accessoren für einen Delegaten, genau wie eine Eigenschaft ein Accessor für ein Feld ist. Eine Eigenschaft erlaubt nur get und set, ein Event erlaubt nur hinzufügen und entfernen.

Damit ist Ihr Code sicher, anderer Code kann nur dann einen Ereignishandler entfernen, wenn er die Ereignishandlermethode und das Zielobjekt kennt. Die C# -Sprache stellt eine zusätzliche Sicherheitsebene dar, da Sie das Zielobjekt nicht benennen können.

Und WinForms schafft eine zusätzliche Sicherheitsschicht, so dass es schwierig wird, selbst wenn Sie Reflection verwenden.Es speichert delegate Instanzen in einem EventHandlerList mit einem geheimen "Cookie" als der Schlüssel, müssen Sie den Cookie kennen, um das Objekt aus der Liste zu graben.

Nun, gehen Sie nicht dorthin. Es ist trivial, Ihr Problem mit einem wenig Code auf Ihrer Seite zu lösen:

private bool mSubscribed; 

private void Subscribe(bool enabled) 
{ 
    if (!enabled) textBox1.VisibleChanged -= textBox1_VisibleChanged; 
    else if (!mSubscribed) textBox1.VisibleChanged += textBox1_VisibleChanged; 

    mSubscribed = enabled; 
} 
+4

Hätten Sie zufällig Blogs, Channel 9-Videos oder msdn-Artikel, die über die Entwurfsmethodik sprechen, die hinter der Erstellung von * events * steckt? Wenn ich vielleicht das * warum * und die * beabsichtigten * Mechanismen verstehen würde, um einige (normalerweise) triviale Dinge zu erreichen, könnte es mir leichter fallen, triviale Lösungen für meine eigenen Problemsätze zu finden. –

+1

Ihre Ereignisse sind nicht schwer zu interagieren. Es ist schwierig, mit den Ereignissen von jemand anderem * zu verkehren, das ist mit privaten Teilen zu tun. Stellen Sie eine Frage dazu. –

+2

Okay, * warum * macht es schwierig, sich mit den Ereignissen anderer zu messen? –

0

Können Sie sich nicht erinnern, ob Sie bereits abonniert haben? Dieser Ansatz hat für mich bisher gut funktioniert. Selbst wenn Sie viele Ereignisse oder Objekte haben, möchten Sie vielleicht immer noch daran denken (zum Beispiel in einem Wörterbuch).

Auf der anderen Seite war Sichtbarkeitsänderung, zumindest für mich, kein guter Punkt zum Abonnieren/Abmelden. Ich gehe typischerweise eher mit Konstruktion/Disposed, die sind klarer als jedes Mal, wenn sich die Sichtbarkeit ändert.

6

Angenommen, Sie haben keinen Zugriff auf die Innereien der Klasse, die das Ereignis deklariert, Sie haben keine Möglichkeit, dies direkt zu tun. Ereignisse geben nur die Operatoren += und -= aus, sonst nichts. Sie benötigen eine Flagge oder einen anderen Mechanismus in Ihrer abonnierenden Klasse, um zu wissen, ob Sie bereits abonniert sind oder nicht.

1

Können Sie die Entscheidungslogik in die Methode einfügen, die das Ereignis auslöst? Unter der Annahme, sind Sie WinForms verwenden würde es in etwa so aussehen:

if (MyEvent != null && isCriteriaFulfilled) 
{ 
    MyEvent(); 
} 

Wo isCriteriaFulfilled von Ihrer sichtbar/unsichtbar Logik bestimmt wird.

// UPDATES /////

Zusätzlich zu Ihrem ersten Kommentar wäre es nicht sinnvoll, das Verhalten in Ihren Event-Handler zu ändern, je nach dem Wert von this.Visible?

a.Delegate += new Delegate(method1); 
... 
private void method1() 
{ 
    if (this.Visible) 
     // Do Stuff 
} 

Oder wenn Sie wirklich gehen müssen mit der Anmeldung und Abmeldung:

private Delegate _method1 = null; 
... 
if(this.visible) 
{ 
    if (_method1 == null) 
     _method1 = new Delegate(method1); 
    a.Delegate += _method1; 
} 
else if (_method1 != null) 
{ 
    a.Delegate -= _method1; 
} 
+1

Ich frage mich, ob 'areCriteriaFulfilled' grammatikalisch besser ist? –

+0

Ich mache das wie folgt if (this.visible) { a.delegate + = neuer Delegate (method1); } sonst { a.Delegate - = neuer Delegate (method1); } – Ram

+0

@Ram: Aktualisierte Antwort. –

1

einfach überprüfen, ob das Steuerelement sichtbar ist oder nicht, wenn der Event-Handler ausgelöst wird.

+0

Ich möchte das nicht tun, da die Ereignisse in regelmäßigen Abständen ausgelöst werden, und ich möchte sie nur verwenden, wenn meine Kontrolle unsichtbar ist. Wenn ich das tue, was du sagst, wird es ein Performance-Hit. – Ram

+0

@Ram: Warum glaubst du, wird es ein Leistungshit sein? Haben Sie die Leistungsänderung gemessen? –

+0

@Phil: Hallo Phil, es ist ein Performance-Hit, wie ich dies mit mehreren Formen und mehrere Ereignisse. Jedes Formular verarbeitet den Datenunterschied. Um die Verarbeitung von Daten zu vermeiden, abonniere ich nur die Ereignisse, deren Form sichtbar ist. Ich glaube, mit Boolean wäre eine gute Option. – Ram

2
/// <summary> 
    /// Determine if a control has the event visible subscribed to 
    /// </summary> 
    /// <param name="controlObject">The control to look for the VisibleChanged event</param> 
    /// <returns>True if the control is subscribed to a VisibleChanged event, False otherwise</returns> 
    private bool IsSubscribed(Control controlObject) 
    { 
    FieldInfo event_visible_field_info = typeof(Control).GetField("EventVisible", 
     BindingFlags.Static | BindingFlags.NonPublic); 
    object object_value = event_visible_field_info.GetValue(controlObject); 
    PropertyInfo events_property_info = controlObject.GetType().GetProperty("Events", 
     BindingFlags.NonPublic | BindingFlags.Instance); 
    EventHandlerList event_list = (EventHandlerList)events_property_info.GetValue(controlObject, null); 
    return (event_list[object_value] != null); 
    } 
0

Ich bin nur auf Hans' Antwort zu erweitern. Ich versuche nur sicherzustellen, dass ich meinen Handler nicht mehr als einmal installiere und ihn nicht entferne, wenn ich ihn noch brauche. Dies schützt nicht vor einem böswilligen oder böswilligen Anrufer, der sich wiederholt abmeldet, für den Sie die Anrufer verfolgen müssten, und das würde Sie nur dazu bringen, dass wiederholte Abonnements den Verfolgungsmechanismus überlaufen.

// Tracks how many times the ReflectionOnlyResolveHandler has been requested. 
private static int _subscribers = 0; 

/// <summary> 
/// Register or unregister the ReflectionOnlyResolveHandler. 
/// </summary> 
/// <param name="enable"></param> 
public static void SubscribeReflectionOnlyResolve(bool enable) 
{ 
    lock(_lock) 
    { 
     if (_subscribers > 0 && !enable) _subscribers -= 1; 
     else if (enable) _subscribers += 1; 

     if (enable && _subscribers == 1) 
      AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ReflectionHelper.ReflectionOnlyResolveHandler; 
     else if (_subscribers == 0) 
      AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= ReflectionHelper.ReflectionOnlyResolveHandler; 
    } 
} 
+0

Die oben genannten Anrufer sind nicht wirklich "Abonnenten", sie bitten nur die Helferklasse, ihren Handler zu registrieren. –

Verwandte Themen