事件监听器是否仅限于只有一个用户?

时间:2010-01-16 16:57:35

标签: c# exception events

是否可以阻止多个订阅者订阅活动?

我已经创建了一个快速示例代码段来给我的问题一些上下文但不幸的是我现在无法测试它,因为我不在我的VS机器上。

目标是:

  • 如果没有订阅者,则返回空列表。
  • 返回单个订户的返回值。
  • 如果多个订阅者尝试订阅该事件,则抛出异常(这是问题的关键)。

这可能吗?

public delegate List<IBaseWindow> GetWindowListDelegate();
public static event GetWindowListDelegate GetWindowListEvent;

public List<IBaseWindow> GetWindowList() {

    if (GetWindowListEvent == null) {
        return new List<IBaseWindow>();
    }

    return GetWindowListEvent();
 }

注意:我正在使用.NET 3.5 sp1。

5 个答案:

答案 0 :(得分:14)

听起来您不需要事件 - 只需公开委托本身并允许调用者自己设置委托引用。

答案 1 :(得分:8)

您可以使用event accessors来完成此操作。如下所示:

  private EventHandler _h;
  public event EventHandler H {
      add {
         if (...) { // Your conditions here.
                    // Warning (as per comments): clients may not
                    // expect problems to occur when adding listeners!
           _h += value;
         }
      }
      remove {
         _h -= value;
      }
  }

正如安德鲁指出的那样,你并不需要事件来完成这个任务。是否有某些特殊原因需要它们?

答案 2 :(得分:6)

只是为了完成John的回答,这是一个只允许一个处理程序的事件的工作实现:

class Foo
{
    private EventHandler _bar;
    public event EventHandler Bar
    {
        add
        {
            if (_bar != null || value.GetInvocationList().Length > 1)
            {
                throw new InvalidOperationException("Only one handler allowed");
            }
            _bar = (EventHandler)Delegate.Combine(_bar, value);
        }
        remove
        {
            _bar = (EventHandler)Delegate.Remove(_bar, value);
        }
    }
}

请注意,公开委托而不是事件不会阻止多个处理程序:由于.NET委托是多播的,因此一个委托可以表示对多个方法的调用。但是,您可以将委托作为属性公开,并在setter中执行与上面代码中相同的检查。

无论如何,正如其他人所指出的那样,防止一个事件的多个处理程序可能不是一个好主意......对于使用它的开发人员来说会非常困惑。

答案 3 :(得分:2)

使用此代码公开YourNameHere委托,但它将禁用+ =功能,允许ONLY =。

private Action<byte[]> yourNameHere;

public Action<byte[]> YourNameHere
{
   set { yourNameHere= value; }
}

希望它有所帮助。

答案 4 :(得分:0)

当然可以实现您想要做的事情,但它不符合惯例 - 我会敦促您提出一个不涉及事件的不同解决方案。

As explained by Jon Skeet,公共事件是多播委托周围的属性类包装器。

您可以将事件的标准实现视为事件发生时要调用的函数列表。

// From the above link:

// the exposed event
public event EventHandler MyEvent

// multicast delegate field
private EventHandler _myEvent;

// property-like add & remove handlers
public event EventHandler MyEvent 
{
    add
    {
        lock (this)
        {
            _myEvent += value;
        }
    }
    remove
    {
        lock (this)
        {
            _myEvent -= value;
        }
    }        
}

...当开发人员看到这样的事件时,他们希望能够没有问题地订阅它,因为事件基本上说“任何类型的人都可以注册接收此事件通知”。

您似乎想要允许某人设置获取窗口列表的实现。我建议做的是允许人们手动传入委托,然后持有单个委托实例。明确表示只有一种方法可以设置它;如果可能的话我建议使用构造函数注入,因为这样可以消除所有的歧义 - 你只能在构造时设置一次委托实例,然后它就不再可以被公共API修改(所以B类不能破坏已经设置的委托)按A)类。

E.g。

public class CallsDelegateToDoSomething
{      
   private Func<List<IBaseWindow>> m_windowLister; 

   public CallsDelegateToDoSomething(Func<List<IBaseWindow>> windowFunc)
   {
       m_windowLister = windowFunc;
   } 

   public List<IBaseWindow> GetWindowList() 
   {    
       if (windowLister == null) 
       {
           return new List<IBaseWindow>();
       }

       return m_windowLister();
   }
}

如果您的设计不允许,请改为创建SetWindowLister(Func<List<IBaseWindow>> windowLister)ClearWindowLister()方法。