In this specific example a null delegate give not a nullReferenceException

T

Tony Johansson

Hello!

Here is a complete simple program example which consist of four classes.
There are two things in this example that are relevant for my question and
that is Timers and delegate. In this program example there is a Timer object
which has a interval set to
1 second. When this timer has elapse it will call the event handler
OnTimedEvent. In this method
handler OnTimedEvent it will call method Notify. In this method Notify it
will call the delegate method that
has been added to the delegate variable tickers. But if no method has been
added do the delegate variable it will be
null. So if I run the program in relese mode and don't add a method to the
delegete variable tickers it will be null but I don't get any
nullReferenceException. When I test and don't add a mehod to the delegete I
just modify the program
and comment out these two lines below
tickers += newMethod; //add method newMethod to delegete
tickers -= oldMethod; //remove method newMethod from delegate

The Timer will still function elapsing every second even if no method has
been added to the delegete variable tickers.
But now to the strange thing if I run the program in debug mode I get a
nullReference Exception.

So as summery why do I get a nullReferenceExcetion in Debug but no in
release. I would be satisfied if I got a nullReferenceException in both
release and debug mode.

I know that a check would be added if the tickers is null.

First you have the class Program below.
//Start class Program
//***************
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Delegates
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
}
}

//Start class Form1
//***************
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;

namespace Delegates
{
partial class Form1 : Form
{
private Clock clock;

public Form1()//C-tor
{
InitializeComponent();
clock = new Clock(digital);
}

private void start_Click(object sender, System.EventArgs e)
{
clock.Start();
}

private void stop_Click(object sender, System.EventArgs e)
{
clock.Stop();
}
}
}

//Start class Clock
//*************
using System.Windows.Forms;

namespace Delegates
{
class Clock
{
private Ticker pulsed = new Ticker();
private TextBox display;

public Clock(TextBox displayBox)//C-tor
{
display = displayBox;
}

public void Start()
{
pulsed.Add(RefreshTime); //Call add to add method
RefreshTime
}

public void Stop()
{
pulsed.Remove(RefreshTime);//call remove to remove method
RefreshTime
}

private void RefreshTime(int hh, int mm, int ss)
{
display.Text = string.Format("{0:D2}:{1:D2}:{2:D2}", hh, mm,
ss);
}
}
}

//Start class Ticker
//*************
namespace Delegates
{
using System.Timers;

class Ticker
{
public delegate void Tick(int hh, int mm, int ss); //delegate
declaration
private Tick tickers; // delegate variable
private Timer ticking = new Timer(); // instansiate Timer

public Ticker() //C-tor
{
ticking.Elapsed += new ElapsedEventHandler(OnTimedEvent); //
eventhandler to be called when timer elapse
ticking.Interval = 1000; // 1 second interval
ticking.Enabled = true;
}

public void Add(Tick newMethod) // delegete type
{
tickers += newMethod; //add method newMethod to delegete
}

public void Remove(Tick oldMethod) //delegete type
{
tickers -= oldMethod; //remove method newMethod from delegate
}

private void Notify(int hours, int minutes, int seconds)
{
tickers(hours, minutes, seconds); //call hidden delegate
method
}

//eventhandler that is called when timer is elapsing
private void OnTimedEvent(object source, ElapsedEventArgs args)
{
int hh = args.SignalTime.Hour;
int mm = args.SignalTime.Minute;
int ss = args.SignalTime.Second;
Notify(hh, mm, ss); // call notify
}
}
}


//Tony
 
J

Jon Skeet [C# MVP]

Tony Johansson said:
Here is a complete simple program example which consist of four classes.

Unfortunately it's not complete. You've not included the designer file
for Form1. In other words, we can't actually compile and run your code.

So as summery why do I get a nullReferenceExcetion in Debug but no in
release. I would be satisfied if I got a nullReferenceException in both
release and debug mode.

How are you checking whether or not you get a NullReferenceException? I
strongly suspect that you're *actually* getting one in both cases, but
the thread pool is just swallowing it in release mode.

If you put a try/catch round the call, showing a message box when
there's a NullReferenceException, I strongly suspect you'll see it in
both modes.
 
T

Tony Johansson

Hello!

You were absolutely right there "Jon" I got nullReferenceException when I
added a try/catch block.
Is this normal that some thread will swallow a null reference or was it just
pure luck in this specific case?
Is this documented that the thread in release mode will swallow this null
reference?

//Tony
 
J

Jon Skeet [C# MVP]

Tony Johansson said:
You were absolutely right there "Jon" I got nullReferenceException when I
added a try/catch block.
Is this normal that some thread will swallow a null reference or was it just
pure luck in this specific case?
Is this documented that the thread in release mode will swallow this null
reference?

Threads in the thread pool normally swallow all exceptions. I'm sure
it's documented somewhere, but I wouldn't like to say exactly where I'm
afraid.
 
P

Peter Duniho

Threads in the thread pool normally swallow all exceptions. I'm sure
it's documented somewhere, but I wouldn't like to say exactly where I'm
afraid.

This is .NET 1.0/1.1 behavior only, actually. With .NET 2.0 this
"backstop" no longer exists.
http://msdn2.microsoft.com/en-us/library/ms228965.aspx

So that suggests that Tony is using a very old version of .NET, or that
he's got set somewhere the "legacyUnhandledExceptionPolicy" configuration
option, that causes newer versions of .NET to revert to old behavior.

AFAIK, in current versions of .NET an exception in a thread pool thread
works the same as in any other thread. I would test this myself right
now, except that Parallels and Mac OS X have conspired yet again to toast
my Windows XP installation, and so I'm not actually in a state where I can
run Windows at the moment, never mind any .NET code. :(

Pete
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
This is .NET 1.0/1.1 behavior only, actually. With .NET 2.0 this
"backstop" no longer exists.
http://msdn2.microsoft.com/en-us/library/ms228965.aspx

Oops, you're right.
So that suggests that Tony is using a very old version of .NET, or that
he's got set somewhere the "legacyUnhandledExceptionPolicy" configuration
option, that causes newer versions of .NET to revert to old behavior.

Or that System.Timers.Timer handles the exception. This appears to be
the case:

<quote>
In the .NET Framework version 2.0 and earlier, the Timer component
catches and suppresses all exceptions thrown by event handlers for the
Elapsed event. This behavior is subject to change in future releases of
the .NET Framework.
</quote>

I guess that's what's doing it in Tony's case. Sorry for the previous
misinformation.
AFAIK, in current versions of .NET an exception in a thread pool thread
works the same as in any other thread.

Yes, it's a shame that the "changes to previous behaviour" section
describes the previous behaviour but not the new behaviour!
 
P

Peter Duniho

[...]
Or that System.Timers.Timer handles the exception. This appears to be
the case:

<quote>
In the .NET Framework version 2.0 and earlier, the Timer component
catches and suppresses all exceptions thrown by event handlers for the
Elapsed event. This behavior is subject to change in future releases of
the .NET Framework.
</quote>

I guess that's what's doing it in Tony's case. Sorry for the previous
misinformation.
AFAIK, in current versions of .NET an exception in a thread pool thread
works the same as in any other thread.

Yes, it's a shame that the "changes to previous behaviour" section
describes the previous behaviour but not the new behaviour!

Yes, and a shame that there doesn't appear to be consistent behavior
across the classes that make use of the thread pool. I knew enough to
look for the general thread pool behavior, but it didn't occur to me that
the Timer class would then override the exception handling (or rather,
lack thereof :) ) of the thread pool.

I would think that for the same reason that having the exception backstop
for thread pool threads was generally a bad idea (and I agree that it
was), that doing so for code called by the Timer class would also be a bad
idea.

Ah well. They do say it's subject to change in the future. Maybe they'll
fix that inconsistency one day. :)

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top