我可以说事件和委托之间的关系采用复合模式吗?

s̮̦*_*̥̳̼ 5 c# delegates design-patterns

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

composite.onTriggered();
// Internally:
// foreach(handler in composite)
// {
//     handler.onTriggered();
// }
Run Code Online (Sandbox Code Playgroud)

将依次调用由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
}
Run Code Online (Sandbox Code Playgroud)

我对抽象级别的想法是否正确?如果没有,你能提供任何更正吗?

Mig*_*boa 3

直接回答你的问题,我会说:不,采用复合模式的事件和委托之间没有关系代表设计是的,它遵循复合模式。事件不。(此外,请注意,您不需要事件来利用代表。(见下文))(我将在最后作为旁注回答您关于“那么这一行的意义是什么? ”的评论)DelegateBased

\n

然而,委托类型本身遵循复合方法,即 \xe2\x80\x9c 复合模式描述了一组对象,这些对象的处理方式与同一类型对象的单个实例相同。\xe2\x80\x9d。

\n

反过来,正如 @Flydog57 和 @mark-seemann 已经提到的,.NET 事件模型遵循观察者模式

\n

事件和委托之间的关系涉及可能需要委托类型 (the )的事件声明,如ECMA-335 (CLI) 第 I 至 VI 部分(标准)的II.18 定义事件部分所述:TypeSpec

\n
\n

在典型用法中,TypeSpec(如果存在)标识一个委托,其签名与传递给 event\xe2\x80\x99s fire 方法的参数匹配。

\n
\n

为了清楚起见,请检查以下两个等效示例,其中EventBased使用不带委托字段的事件DelegateBased使用不带事件的委托字段。请注意,我明确表示委托字段委托类型。他们不一样。这两个示例都需要一个委托类型,在本示例中声明如下:

\n
delegate void Observer();\n
Run Code Online (Sandbox Code Playgroud)\n

您可以使用以下命令运行这两个示例:

\n
var subject = new DelegateBased(); // replace it with: var subject = new EventBased();\nObserver foo = () => Console.Write("Foo");\nObserver bar = () => Console.Write("Bar");\nsubject.RegisterObserver(foo); // subject.Caller += foo;\nsubject.RegisterObserver(bar); // subject.Caller += bar;\nsubject.Notify(); // prints: FooBar\nConsole.WriteLine();\nsubject.UnregisterObserver(foo); // subject.Caller -= foo;\nsubject.Notify(); // prints: Bar\n
Run Code Online (Sandbox Code Playgroud)\n

接下来,根据维基百科中观察者模式EventBased的示例,使用名称的和的两个实现DelegateBased

\n
class EventBased {\n  private List<Observer> observers = new List<Observer>();\n  public event Observer Caller {\n    add { RegisterObserver(value); }\n    remove { UnregisterObserver(value); }\n  }\n  public void Notify() { foreach (var caller in observers) caller(); }\n    \n  public void RegisterObserver(Observer val) {  observers.Add(val); }\n    \n  public void UnregisterObserver(Observer val) { observers.Remove(val); }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
class DelegateBased {\n  private Observer observers; // delegate field without events\n    \n  public void Notify() { observers(); }\n\n  public void RegisterObserver(Observer val) { \n    observers = (Observer) Delegate.Combine(observers, val); // <=> observers += val\n  }\n  public void UnregisterObserver(Observer val) {\n    observers = (Observer) Delegate.Remove(observers, val); // <=> observers -= val\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

关于您的评论:

\n
EventHandler handler = ThresholdReached; // So what\'s the point of this line?\nhandler?.Invoke(this, e);\n
Run Code Online (Sandbox Code Playgroud)\n

Jeffrey Richter 在其杰作“Clr via C#”第 11 章“以线程安全方式引发事件”中的事件中明确指出了其原因(请参阅NewMail示例ThresholdReached的 ),其中指出:

\n
\n

该方法的问题OnNewMail在于,线程可以看到NewMail不为 null,然后,就在调用 之前NewMail,另一个线程可以从链中删除委托NewMail null,从而导致NullReferenceException抛出异常。

\n
\n