Tuesday, August 19, 2008

Delegates and events. What does `event` keyword actually mean in C#

In a very interesting "Accelerated C# 2008" book, its author Trey Nash described the role of C# events (page 262):
"... .NET runtime designers were so generous as to define a formalized built-in event mechanism. When you declare an event within a class, internally the compiler implements some hidden methods that allow you to register and unregister delegates, which are called when a specific event is raised. In essence, an event is a shortcut that saves you the time of having to write the register and unregister methods that manage a delegate chain yourself."
Then, on a page 265 he is saying:
"... events are ideal for implementing a publish/subscribe design pattern, where many listeners are registering for notification (publication) of an event. Similarly, you can use .NET events to implement a form of the Observer pattern, where various entities register to receive notifications that some other entity has changed."

Often, when some material is not clear to me, I try to read another book to clarify a matter. So, I found the following description of events in "C# 3.0 in a Nutshell" book (page 112:
"When using delegates, two emergent roles commonly appear: broadcaster and subscriber.
The broadcaster is a type that contains a delegate field. The broadcaster decides when to broadcast, by invoking the delegate.
The subscribers are the method target recipients. A subscriber decides when to start and stop listening, by calling += and -= on the broadcaster's delegate. A subscriber does not know about, or interfere with, other subscribers.
Events are a language feature that formalizes this pattern. An event is a wrapper for a delegate that exposes just the subset of delegate features required for the broadcaster/subscriber model. The main purpose of events is to prevent subscribers from interfering with each other."

This is quite opposite to what Trey said about events! They are introduced to prevent subscribers from interfering with each other, not as a shortcut that saves you the time of having to write the register and unregister methods.
+= and -= operators are defined for non-event delegates as well as for event delegates, while non-event delegates also expose assignment (=) operator and GetInvocationList() method which are too dangerous for subscribers. They allow a particular subscriber to investigate which other classes also subscribed to the same delegate and even allow to remove other independent subscribers. That's exactly what we prevent by adding an `event` keyword to a delegate.

Here's a modified code snippet from "C# 3.0 in a Nutshell" which proves what I said above (note, that it does not use famous EventHandler<TEventArgs> delegates; events could actually be used with any delegates):


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace Chp04_events {
 
  internal delegate void PriceChangedHandler( decimal oldPrice, decimal newPrice );
 
  /// <summary>
  /// "Broadcaster" class
  /// </summary>
  internal class Stock {
    string _symbol;
    decimal _price;
 
    public Stock( string symbol ) { this._symbol = symbol; }
 
    // If you comment out this line and uncomment next line,  "myStock.PriceChanged = ..." and "Delegate[] chain = " statements
    // below in public void SetStockToWatch( Stock myStock ) methods would become legal, compromising an independence of event subscribers.
    // That's why we use an `event` keyword, rather than simpli a delegate.
    public event PriceChangedHandler PriceChanged;
    //public PriceChangedHandler PriceChanged;
 
    public decimal Price {
      get { return _price; }
      set {
        if (_price == value) return;
        if (PriceChanged != null) {
          // Code within the Broadcaster type has full access to event and can treat it as delegate:
          Delegate[] chain = PriceChanged.GetInvocationList();
          Console.WriteLine( "There is/are {0} subscribers watching for event I'm broadcsting", chain.Length );
          Console.WriteLine( "The first subscriber has a type of {0}", chain[0].Target.GetType() );
          if (chain.Length > 1) {
            Console.WriteLine( "The second subscriber has a type of {0}", chain[1].Target.GetType() );
          }
 
          PriceChanged( _price, value );
        }
        _price = value;
      }
    }
  }
 
  /// <summary>
  /// "Subscriber" class
  /// </summary>
  internal class StockWatcher {
    protected string _watcherName = String.Empty;
 
    public StockWatcher( string watcherName ) {
      this._watcherName = watcherName;
    }
 
    public string WatcherName {
      get { return _watcherName; }
    }
 
    public void SetStockToWatch( Stock myStock ) {
      myStock.PriceChanged += new PriceChangedHandler( myStock_PriceChanged );
      // Code outside the Broadcaster type can only perform += an -=. "Subscriber" does not know about other "subscribers".
      // The following line of code won't compile: 
      // Error: The event 'Chp04_events.Stock.PriceChanged' can only appear on the left hand side of += or -=
      // (except when used from within the type 'Chp04_events.Stock')
      myStock.PriceChanged = new PriceChangedHandler( myStock_PriceChanged );
      // The same error:
      Delegate[] chain = myStock.PriceChanged.GetInvocationList();
    }
 
    public void myStock_PriceChanged( decimal oldPrice, decimal newPrice ) {
      Console.WriteLine( "{0}: Stock price was changed from {1} to {2}", WatcherName, oldPrice, newPrice );
    }
  }
 
  internal class NewStockWatcher : StockWatcher {
    public NewStockWatcher( string watcherName ) : base( watcherName ) { }
  }
 
  class Program {
    static void Main( string[] args ) {
      decimal myDecPrice;
 
      var myStock = new Stock( "Napster" );
      var myStockWatcher1 = new StockWatcher( "First Watcher" );
      var myStockWatcher2 = new NewStockWatcher( "Second Watcher" );
 
      myStockWatcher1.SetStockToWatch( myStock );
      myStockWatcher2.SetStockToWatch( myStock );
 
      while (1 == 1) {
        Console.WriteLine( "Please enter new stock price:" );
        string newStockPrice = Console.ReadLine();
        if (newStockPrice == String.Empty) break;
        if (Decimal.TryParse( newStockPrice, out myDecPrice ))
          myStock.Price = myDecPrice;
      }
    }
  }
}

Friday, August 15, 2008

"ASP.NET gets no Respect" by Rick Strahl

There is a good ASP.NET gets no Respect article just posted by well-known ASP.NET developer Rick Strahl. Read it! I commented there too :)

Thursday, August 14, 2008

A novice Scala programmer: Eclipse vs. IntelliJ IDEA vs. Netbeans. Debugger and for () yield {}

I continue my Scala IDE mini series I described my experience with installation of Eclipse, IntelliJ IDEA, and Netbeans applications and installation of Scala plugins in those environments.

The following simple code (from Programming in Scala, chapter 7) poses certain challendges to Scala debuggers. Some debuggers cannot stop on breakpoint placed, for example, on "val prod = (i * j).toString" line.
As Alexander Podkhalyuzin from JetBRAINS/Intellij IDEA Scala Plugin team said, "This problem goes to Java debugger problem with anonymous classes, because it's compile in different class file. So, we must to understand what name of this class files, and to set to each line, line in this class file. In Eclipse developers do this work, we have not implemented this yet, but you must be sure, it will be, because it is not convenient to work without for debugger."

As of today, Eclipse Scala plugin debugger can stop, the latest Intellij IDEA Scala Plugin debugger can stop as well. Both go even to a library Seq.class; Eclipse shows its code, IntelliJ IDEA shows interface, saying that it is "IntelliJ API Decompiler stub source generated from a class file. Implementation of methods is not available".
The latest and greatly improved NetBeans Scala plugin debugger still cannot stop here.


package chp7multitable
object Main {
/**
* @param args the command line arguments
*/
def main(args: Array[String]) = {
val multi = {
val table = for (i <- 1 to 10) yield {
val row = for (j <- 1 to 10) yield {
° val prod = (i * j).toString
String.format("%4s", Array(prod))
}
row.mkString + "\n"
}
table.mkString
}
println(multi)
}
}



1. Eclipse vs. IntelliJ IDEA vs. Netbeans. Installation.
2. Eclipse vs. IntelliJ IDEA vs. Netbeans. Creating and running Scala project with Eclipse.
3. Eclipse vs. IntelliJ IDEA vs. Netbeans. Creating and running Scala project with IntelliJ IDEA.
4. Eclipse vs. IntelliJ IDEA vs. Netbeans. Creating and running Scala project with Netbeans.
5. A novice Scala programmer: Eclipse vs. IntelliJ IDEA vs. Netbeans.
6. A novice Scala programmer: Eclipse vs. IntelliJ IDEA vs. Netbeans. Debugger and for () yield {}.

Thursday, August 07, 2008

Effective Java Programming

Here's an interesting speech by Joshua Bloch (the author of Effective Java) on YouTube.

(I use ScribeFire with Firefox to post on Blogger. Unfortunately, ScribeFire always posts as Draft, regardless of me selecting or un-selecting post-to-draft option.)

Saturday, August 02, 2008

A couple of excellent Scala resources

Here's a very live and professional Scala forum. I asked a question there and immediately got a qualified answer from this wonderful Scala pro.

BTW.: I'm writing this post using this tool. Seems to be more convenient that Blogger itself.