Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I say that the relation between events and delegates adopts composite pattern?

An event can contain many handlers which are defined using delegate, my current understanding is that delegate is just an abstraction of function pointer. Since an event, which associated with a delegate type, can add / remove many delegates into it, and composite pattern treats a composite object the same as the terminal object, so the idea is:

composite.onTriggered();
// Internally:
// foreach(handler in composite)
// {
//     handler.onTriggered();
// }

will in turn call every handlers managed by the composite.

But it seems like public event EventHandler ThresholdReached does not define a composite, see my comment in the code below

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
}

Am I correct about the idea in abstract level? If not could you provide any correction(s)?

like image 355
Rainning Avatar asked Nov 19 '20 05:11

Rainning


People also ask

How delegates and events are related?

Delegate is a type that defines a signature and holds a reference of method whose signature matches with the delegate. Event is a notification raised by an object to signal the occurrence of an action. Delegate is associated with the event to hold a reference of a method to be called when the event is raised.

What is the main difference between the event and delegate?

An event is declared using the event keyword. Delegate is a function pointer. It holds the reference of one or more methods at runtime. Delegate is independent and not dependent on events.

What is the difference between events and multicast delegates?

Multicast delegates help to invoke multiple callbacks. Events encapsulate delegate and implement publisher and subscriber model.

Why do we need events when we have delegates?

Yes, Events! This is a simplified explanation on why we use events even though we can already use delegates: To provide encapsulation and not exposing business logic. To prevent Team Client from clearing all assign methods to delegates (You cannot do that for events):

What is the difference between event and delegate?

Event is a notification raised by an object to signal the occurrence of an action. Delegate is associated with the event to hold a reference of a method to be called when the event is raised. In the next article we will look at the Observer and Pub-Sub pattern which is based on the Event-Delegate Model.

What is the purpose of the composite pattern?

The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.

What are events and delegates in C #?

Events and Delegates in C# are undoubtedly among the most confusing topics. Let us explore more about these. The code used in this article can be downloaded from GitHub. Delegates are function pointers. Let’s write some code using delegates to understand delegates.

What is the difference between composite and elementary events?

Composite or Compound Events Events which can be further sub-divided into (or viewed in the form of) smaller events are called composite events or compound events. Events which cannot be further sub - divided into smaller events are called elementary or simple events.


2 Answers

Directly answering your question, I would say: No, there is no relation between events and delegates adopting the composite pattern. Delegates design yes, it follows the composite pattern. Events not. (Moreover, notice that you do not need events to take advantage of delegates. (see DelegateBased bellow)) (I will answer your comment about "So what's the point of this line?" at the end as a side note)

Nevertheless, the Delegate type itself follows the composite approach in terms that “The composite pattern describes a group of objects that are treated the same way as a single instance of the same type of object.”.

In turn, as @Flydog57 and @mark-seemann have already mentioned the .NET event model follows the observer pattern.

The relation between Events and Delegates regards the Event declaration that may require a Delegate type (the TypeSpec), as it is stated in the section II.18 Defining events of ECMA-335 (CLI) Partitions I to VI (the standard):

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

To make it clear, check the following two equivalent examples where EventBased uses events without a delegate field and DelegateBased uses a delegate field without events. Notice that I say distinctly delegate field or delegate type. They are not the same. Both examples need a delegate type which is declared as the following for this example:

delegate void Observer();

You may run both examples with:

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

Next, the two implementations of EventBased and DelegateBased that use names according to the example of 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
  }
}

Regarding your comment about:

EventHandler handler = ThresholdReached; // So what's the point of this line?
handler?.Invoke(this, e);

The reason it is clearly identified by Jeffrey Richter in its masterpiece "Clr via C#" in Chapter 11 - Events at "Raising an Event in a Thread-Safe Way" (see the NewMail as the ThresholdReached of your example) where it states:

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.

like image 86
Miguel Gamboa Avatar answered Sep 30 '22 02:09

Miguel Gamboa


As Flydog57 points out, the .NET event model is essentially the Observer pattern built into the language, just like IEnumerable and foreach implements the Iterator pattern.

The patterns in the Gang of Four book are, however, at various levels of abstraction. I'm not sure that this was clear to anyone in 1994, but with decades of use it's becoming increasingly clear (to me, at least) that some of those patterns are more general than others. One such pattern is the Adapter pattern, where you can view the Decorator pattern as a degenerate specialisation.

Another such pattern is Composite. You can view some of the other patterns in the books as specialisations of Composite. Not only Observer, but also Command and State (at least as described in the book); possibly others.

I think that your intuition is correct. While events are most specifically patterned after the Observer pattern, you can also think of it as a Composite. This may be clearer if we consider Reactive Extensions (Rx) and the IObserver<T> interface instead of .NET events. IIRC, Rx defines conversions between .NET events and its own model.

More generally, every API that gives rise to a monoid can be modelled as a Composite. Since events return no data (they have void method signatures) they form a monoid. Therefore, you can also view them as an instance of the Composite design pattern.

like image 40
Mark Seemann Avatar answered Sep 30 '22 03:09

Mark Seemann