views:

781

answers:

3

Not sure what exactly is going on here, but seems like in .NET 1.1 an uninitialized event delegate can run without issues, but in .NET 2.0+ it causes a NullReferencException. Any ideas why. The code below will run fine without issues in 1.1, in 2.0 it gives a NullReferenceException. I'm curious why does it behave differently? What changed?

Thanks

eg

class Class1
    {
 public delegate void ChartJoinedRowAddedHandler(object sender);



 public static event ChartJoinedRowAddedHandler ChartJoinedRowAdded;
 public static DataTable dt;

 public static void Main()
 {   
  dt = new DataTable();
  dt.RowChanged += new DataRowChangeEventHandler(TableEventHandler);

  object [] obj = new object[]{1,2};
  dt.Columns.Add("Name");
  dt.Columns.Add("Last");
  dt.NewRow();
  dt.Rows.Add(obj);
 }

 private static void TableEventHandler(object sender, DataRowChangeEventArgs e)
 {
  ChartJoinedRowAdded(new object());
 }
}
+1  A: 

[updated] AFAIK, there was no change here to the fundamental delegate handling; the difference is in how DataTable behaves.

However! Be very careful using static events, especially if you are subscribing from instances (rather than static methods). This is a good way to keep huge swathes of objects alive and not be garbage collected.

Running the code via csc from 1.1 shows that the general delegate side is the same - I think the difference is that the DataTable code that raises RowChanged was swallowing the exception. For example, make the code like below:

    Console.WriteLine("Before");
    ChartJoinedRowAdded(new object());
    Console.WriteLine("After");

You'll see "Before", but no "After"; an exception was thrown and swallowed by the DataTable.

Marc Gravell
+1  A: 

The eventhandler system is basically just a list of functions to call when a given event is raised.

It initializes to the "null" list, and not the empty list, so you need to do

if (ChartJoinedRowAdded != null)
      ChartJoinedRowAdded(new object())
Soraz
A: 

The way events work hasn't really changed from 1.1 to 2

Although the syntax looks like normal aggregation it really isn't:

dt.RowChanged += TableEventHandler;
dt.RowChanged += null;
dt.RowChanged += delegate (object sender, DataRowChangeEventArgs e) {
    //anon
};

Will fire TableEventHandler and then the delegate - the null is just skipped.

You can use null to clear events, but only inside the event firing class:

this.MyEvent = null;

If nothing subscribes your event will be null - see soraz's answer. The DataTable class will contain a similar check and won't fire the event if there are no subscribers.

The standard pattern is:

//events should just about always use this pattern: object, args
public static event EventHandler<MyEventArgs> ChartJoinedRowAdded;


//inheriting classes can override this event behaviour
protected virtual OnChartJoinedRowAdded() {
    if( ChartJoinedRowAdded != null )
        ChartJoinedRowAdded( this, new MyEventArgs(...) );
}
Keith