c# - 我可以说事件和委托(delegate)之间的关系采用复合模式吗?

标签 c# design-patterns delegates

一个事件可以包含许多使用 delegate 定义的处理程序。 ,我目前的理解是委托(delegate)只是函数指针的抽象。由于 event ,与 delegate 相关联类型,可以在其中添加/删除许多委托(delegate),并且复合模式将复合对象视为终端对象,因此想法是:

composite.onTriggered();
// Internally:
// foreach(handler in composite)
// {
//     handler.onTriggered();
// }
将依次调用 composite 管理的每个处理程序.
但似乎 public event EventHandler ThresholdReached没有定义复合,请参阅下面代码中的我的评论
class Counter
{
    public event EventHandler ThresholdReached;

    protected virtual void OnThresholdReached(EventArgs e)
    {
        EventHandler handler = ThresholdReached; // So what's the point of this line?
        handler?.Invoke(this, e);
        // Why not just:
     // ThresholdReached?.Invoke(this, e);
    } 

    // provide remaining implementation for the class
}
我对抽象级别的想法是否正确?如果不能,您能否提供任何更正?

最佳答案

直接回答你的问题,我会说:不,没有关系 在采用复合模式的事件和委托(delegate)之间。 委托(delegate)设计是 ,它遵循复合模式。 事件不是 . (此外,请注意 您不需要事件来利用委托(delegate)。(见 DelegateBased 下文))(我将在最后回答您关于“那么这条线的意义何在?”的评论作为旁注)
尽管如此,委托(delegate)类型它本身遵循复合方法,即“复合模式描述了 对象组处理方式与 相同单实例属于同一类型的对象。”。
反过来,正如@Flydog57 和@mark-seemann 已经提到的.NET 事件模型遵循观察者模式。
事件和委托(delegate)之间的关系关于 事件声明这可能需要一个委托(delegate)类型(TypeSpec),如第 II.18 节定义 ECMA-335 (CLI) Partitions I to VI 的事件中所述(标准):

In typical usage, the TypeSpec (if present) identifies a delegate whose signature matches the arguments passed to the event’s fire method.


为了清楚起见,请检查以下两个等效示例,其中 EventBased使用没有委托(delegate)字段和 DelegateBased 的事件使用没有事件的委托(delegate)字段。请注意,我明确表示委托(delegate)字段或委托(delegate)类型。它们是不相同的。这两个示例都需要一个委托(delegate)类型,在此示例中声明如下:
delegate void Observer();
您可以使用以下命令运行这两个示例:
var subject = new DelegateBased(); // replace it with: var subject = new EventBased();
Observer foo = () => Console.Write("Foo");
Observer bar = () => Console.Write("Bar");
subject.RegisterObserver(foo); // subject.Caller += foo;
subject.RegisterObserver(bar); // subject.Caller += bar;
subject.Notify(); // prints: FooBar
Console.WriteLine();
subject.UnregisterObserver(foo); // subject.Caller -= foo;
subject.Notify(); // prints: Bar
接下来是EventBased的两种实现和 DelegateBased根据 Observer Pattern in Wikipedia 的示例使用名称
class EventBased {
  private List<Observer> observers = new List<Observer>();
  public event Observer Caller {
    add { RegisterObserver(value); }
    remove { UnregisterObserver(value); }
  }
  public void Notify() { foreach (var caller in observers) caller(); }
    
  public void RegisterObserver(Observer val) {  observers.Add(val); }
    
  public void UnregisterObserver(Observer val) { observers.Remove(val); }
}
class DelegateBased {
  private Observer observers; // delegate field without events
    
  public void Notify() { observers(); }

  public void RegisterObserver(Observer val) { 
    observers = (Observer) Delegate.Combine(observers, val); // <=> observers += val
  }
  public void UnregisterObserver(Observer val) {
    observers = (Observer) Delegate.Remove(observers, val); // <=> observers -= val
  }
}

关于您的评论:
EventHandler handler = ThresholdReached; // So what's the point of this line?
handler?.Invoke(this, e);
Jeffrey Richter 在其杰作“Clr via C#”在第 11 章 - “以线程安全的方式引发事件”中的事件(参见 NewMail 作为示例的 ThresholdReached)中明确指出了它的原因,其中它指出:

The problem with the OnNewMail method is that the thread could see that NewMail is not null, and then, just before invoking NewMail, another thread could remove a delegate from the chain making NewMail null, resulting in a NullReferenceException being thrown.

关于c# - 我可以说事件和委托(delegate)之间的关系采用复合模式吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64906053/

相关文章:

.net - 获取自定义用户控件中声明的事件的所有事件处理程序

c# - Prism 6 UriQuery 类丢失?

c# - 将通用方法参数转换为特定类型

c# - 为什么要断开与数据库的连接?

design-patterns - 具有非同事类的调解器模式

c++ - 在 C++ 工厂方法的实现中遇到继承问题

iphone - 如何检查 'destructive' 按钮是否被按下(UIActionSheet - iPhone)?

swift - 委托(delegate)从 subview 到主视图的更改

c# - 查找使用 WCF net.tcp 发送消息所花费的时间

grails - Grails:可以对命令对象使用创建/保存吗?