如何确定事件是否已订阅

时间:2010-04-23 08:45:53

标签: c# .net

在我的.NET应用程序中,我订阅了另一个类的事件。订阅是有条件的。我在控件可见时订阅事件,在它变得不可见时取消订阅。但是,在某些情况下,即使控件不可见,我也不想取消订阅事件,因为我想要在后台线程上发生操作的结果。

有没有办法确定一个班级是否已经订阅了该活动?

我知道我们可以在类中通过检查null的事件来引发该事件,但是如何在订阅该事件的类中执行此操作?

7 个答案:

答案 0 :(得分:61)

明确发明 event 关键字是为了阻止您做您想做的事情。它限制对底层delegate对象的访问,因此没有人可以直接处理它存储的事件处理程序订阅。事件是委托的访问者,就像属性是字段的访问者一样。属性只允许获取和设置,事件只允许添加和删除。

这使代码保持安全,其他代码只有在知道事件处理程序方法和目标对象时才能删除事件处理程序。 C#语言通过不允许您命名目标对象来增加额外的安全层。

WinForms提供了额外的安全层,因此即使您使用Reflection也会变得困难。它将delegate实例存储在EventHandlerList中,并以秘密“cookie”作为密钥,您必须知道cookie才能将对象从列表中挖出。

嗯,不要去那里。用你的一些代码来解决你的问题是微不足道的:

private bool mSubscribed;

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

    mSubscribed = enabled;
}

答案 1 :(得分:6)

假设您无法访问声明该事件的类的内部,您无法直接执行此操作。事件仅公开运算符+=-=,而不是其他任何内容。您需要在订阅课程中使用标记或其他机制来了解您是否已经订阅。

答案 2 :(得分:3)

  /// <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);
  }

答案 3 :(得分:1)

您能否将决策逻辑放入触发事件的方法中?假设您正在使用Winforms,它看起来像这样:

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

isCriteriaFulfilled由您的可见/不可见逻辑确定。

//更新/////

根据this.Visible的值,改变你的事件处理程序中的行为是不是有意义?

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

或者如果你真的需要订阅和取消订阅:

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

答案 4 :(得分:1)

只要触发事件处理程序,只需检查控件是否可见。

答案 5 :(得分:0)

难道你不记得你是否已经订阅了?到目前为止,这种方法对我来说很好。即使您有很多事件或对象,您仍然可能只想记住它(例如,在字典中)。

另一方面,至少在我看来,可见性变化不是订阅/取消订阅的好点。我通常宁愿选择施工/处置,这比每次能见度变化都要清晰。

答案 6 :(得分:0)

我只是在扩展Hans&#39;回答。我只是想确保我不会多次安装我的处理程序,而不是在我仍需要它时将其删除。这并不能防止恶意或恶意呼叫者反复取消订阅,因为您需要跟踪呼叫者,这只会让您重复订阅超出跟踪机制。

// 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;
    }
}