直接回答您的问题,我会说:不,采用复合模式的事件和代表之间没有关系。 委托设计是的,它遵循复合模式。 事件不是。 (此外,请注意您不需要事件来利用代表。(请参阅DelegateBased 下文))(我将回答您关于“此行的意义何在? ”作为附注)
尽管如此,委托类型本身遵循复合方法,“复合模式描述了一个对象组,这些对象的处理方式与单个实例是同一类型的对象。”。
反过来,正如@Flydog57 和@mark-seemann 已经提到的那样,.NET 事件模型遵循观察者模式。
事件和委托之间的关系涉及可能需要委托类型(TypeSpec)的事件声明,如 部分所述II.18 定义ECMA-335 (CLI) Partitions I to VI 的事件(标准):
在典型用法中,TypeSpec(如果存在)标识其签名与传递给事件的 fire 方法的参数匹配的委托。
为了清楚起见,请检查以下两个等效示例,其中EventBased 使用没有委托字段的事件,DelegateBased 使用没有事件的委托字段。请注意,我明确地说 委托字段 或 委托类型。它们是不相同的。这两个例子都需要一个委托类型,在这个例子中声明如下:
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)其中声明:
OnNewMail 方法的问题是线程可以看到NewMail 不为空,然后,就在调用NewMail 之前,另一个线程可以从链中删除一个委托NewMail@987654340 @,导致NullReferenceException 被抛出。