Subscribing for events using lambda expressions

In C#, an object or a class can be used to inform other objects or classes when something happens, which is known as an event. There are two kinds of classes in the event, they are publishers and subscribers. The publisher is a class or object that sends (or raises) the event, while the subscriber is a class or object that receives (or handles) the event. Fortunately, lambda expressions can also be used to handle events. Let's take a look at the following code to discuss events further:

public class EventClassWithoutEvent 
{ 
  public Action OnChange { get; set; } 
  public void Raise() 
  { 
    if (OnChange != null) 
    { 
      OnChange(); 
    } 
  } 
} 

The preceding code can be found in the EventsInLambda.csproj project. As we can see, a class named EventClassWithoutEvent has been created in the project. The class has a property named OnChange. This property's role is to store the action that subscribes the class and will be run when the Raise() method is invoked. Now, let's consume the Raise() method using the following code:

public partial class Program 
{ 
  private static void CreateAndRaiseEvent() 
  { 
    EventClassWithoutEvent ev = new EventClassWithoutEvent(); 
    ev.OnChange += () => 
      Console.WriteLine("1st: Event raised"); 
    ev.OnChange += () => 
      Console.WriteLine("2nd: Event raised"); 
    ev.OnChange += () => 
      Console.WriteLine("3rd: Event raised"); 
    ev.OnChange += () => 
      Console.WriteLine("4th: Event raised"); 
    ev.OnChange += () => 
      Console.WriteLine("5th: Event raised"); 
    ev.Raise(); 
  } 
} 

If we run the preceding CreateAndRaiseEvent() method, we will retrieve the following output on the console:

Subscribing for events using lambda expressions

From the code, we can see that when we invoke the CreateAndRaiseEvent() method, the code instances an EventClassWithoutEvent class. It then subscribes to the event with five different methods inside the lambda expression and then raises the event by invoking the Raise() method. The following code snippet will explain this further:

EventClassWithoutEvent ev = new EventClassWithoutEvent(); 
ev.OnChange += () => 
  Console.WriteLine("1st: Event raised"); 
ev.Raise(); 

From the preceding code snippet, we can see that the lambda expression can be used to subscribe to the event since it uses a delegate to store the subscribed method. However, there is still a weakness in the preceding code. Take a look at the last OnChange assignment from this code:

ev.OnChange += () => 
  Console.WriteLine("5th: Event raised"); 

Now, suppose that we change it to this:

ev.OnChange = () => 
  Console.WriteLine("5th: Event raised"); 

Then, we will remove all four previous subscribers. Another weakness is that EventClassWithoutEvent raises the event but nothing can stop the users of the class from raising this event. By invoking OnChange(), all users of the class can raise the event to all subscribers.

Using the event keyword

The use of the event keyword can solve our preceding problem since it will enforce the users of the class to subscribe something only using either the += or -= operator. Let's take a look at the following code to explain this further:

public class EventClassWithEvent 
{ 
  public event Action OnChange = () => { }; 
  public void Raise() 
  { 
    OnChange(); 
  } 
} 

From the preceding code, we can see that we are no longer using a public property but a public field in the EventClassWithEvent class. Using the event keyword, the compiler will secure our field from unwanted access. The event keyword will also protect the subscription list since it cannot be assigned to any lambda expression using the = operator but has to be used with the += or -= operator. Now, let's take a look at the following code to prove this:

public partial class Program 
{ 
  private static void CreateAndRaiseEvent2() 
  { 
    EventClassWithEvent ev = new EventClassWithEvent(); 
    ev.OnChange += () => 
      Console.WriteLine("1st: Event raised"); 
    ev.OnChange += () => 
      Console.WriteLine("2nd: Event raised"); 
    ev.OnChange += () => 
      Console.WriteLine("3rd: Event raised"); 
    ev.OnChange += () => 
      Console.WriteLine("4th: Event raised"); 
    ev.OnChange = () => 
      Console.WriteLine("5th: Event raised"); 
    ev.Raise(); 
  } 
} 

We now have a method named CreateAndRaiseEvent2(), which is exactly same as the CreateAndRaiseEvent() method except that the last OnChange assignment used the = operator instead of the += operator. However, since we have applied the event keyword to the OnChange field, the code cannot be compiled and the CS0070 error code will occur, as shown in the following screenshot:

Using the event keyword

There is no risk anymore since the event keyword has restricted the use of the = operator. The event keyword also prevents the outside user of the class from raising the event. Only the part of the class that defines the event can raise the event. Let's take a look at the difference between the EventClassWithoutEvent and EventClassWithEvent class:

public partial class Program 
{ 
  private static void CreateAndRaiseEvent3() 
  { 
    EventClassWithoutEvent ev = new EventClassWithoutEvent(); 
    ev.OnChange += () => 
      Console.WriteLine("1st: Event raised"); 
    ev.OnChange += () => 
      Console.WriteLine("2nd: Event raised"); 
    ev.OnChange += () => 
      Console.WriteLine("3rd: Event raised"); 
    ev.OnChange(); 
    ev.OnChange += () => 
      Console.WriteLine("4th: Event raised"); 
    ev.OnChange += () => 
      Console.WriteLine("5th: Event raised"); 
    ev.Raise(); 
  } 
} 

The reference of the preceding CreateAndRaiseEvent3() method is CreateAndRaiseEvent(), but we insert ev.OnChange(); in between the third event and fourth event. If we run the method, it will run successfully, and we will see the following output on the console:

Using the event keyword

As we can see from the output, OnChange() in the EventClassWithoutEvent class can raise the event. Compared to the EventClassWithEvent class, if we insert OnChange() between any subscribing event, the compiler will create a compile error, as shown in the following code:

public partial class Program 
{ 
  private static void CreateAndRaiseEvent4() 
  { 
    EventClassWithEvent ev = new EventClassWithEvent(); 
    ev.OnChange += () => 
      Console.WriteLine("1st: Event raised"); 
    ev.OnChange += () => 
      Console.WriteLine("2nd: Event raised"); 
    ev.OnChange += () => 
      Console.WriteLine("3rd: Event raised"); 
    ev.OnChange(); 
    ev.OnChange += () => 
      Console.WriteLine("4th: Event raised"); 
    ev.OnChange += () => 
      Console.WriteLine("5th: Event raised"); 
    ev.Raise(); 
  } 
} 

If we compile the preceding code, we will get the CS0070 error code again, since we insert ev.OnChange(); in between the third event and the fourth event.

Using EventHandler or EventHandler<T>

Actually, C# has a class named EventHandler or EventHandler<T> that we can use to initialize an event instead of using an Action class. An EventHandler class takes a sender object and event arguments. The sender is the object that raises the event. Using EventHandler<T>, we can define the type of event arguments. Let's take a look at the following code, which we can find in the EventWithEventHandler.csproj project:

public class MyArgs : EventArgs 
{ 
  public int Value { get; set; } 
  public MyArgs(int value) 
  { 
    Value = value; 
  } 
} 
public class EventClassWithEventHandler 
{ 
  public event EventHandler<MyArgs> OnChange = 
    (sender, e) => { }; 
  public void Raise() 
  { 
    OnChange(this, new MyArgs(100)); 
  } 
} 

We have two classes, named MyArgs and EventClassWithEventHandler. The EventClassWithEventHandler class uses EventHandler<MyArgs>, which defines the event argument's type. We need to pass an instance of MyArgs when raising the event. Subscribers of the event can access the arguments and use them. Now, let's take a look at the following CreateAndRaiseEvent() method code:

public partial class Program 
{ 
  private static void CreateAndRaiseEvent() 
  { 
    EventClassWithEventHandler ev = 
      new EventClassWithEventHandler(); 
    ev.OnChange += (sender, e) 
      => Console.WriteLine( 
          "Event raised with args: {0}", e.Value); 
    ev.Raise(); 
  } 
} 

If we run the preceding code, we will get the following output on the console:

Using EventHandler or EventHandler<T>

From the preceding code, we can see that the lambda expression plays its role to subscribe to an event, as follows:

ev.OnChange += (sender, e) 
  => Console.WriteLine( 
      "Event raised with args: {0}", e.Value); 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset