By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,676 Members | 1,636 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,676 IT Pros & Developers. It's quick & easy.

Raising events

P: n/a
Can someone explain why this code DOES NOT raise a null reference exception?
//////////////////////////// Program.cs
/////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Text;
using CSharpLib;

namespace CSharpConsole
{
class Program
{
private EventHandler handler;
private Class1 obj;

static void Main(string[] args)
{
(new Program()).RunTest();
Console.ReadKey(true);
}

private Program()
{
obj = new Class1();
handler = new EventHandler(DoSomething);
}

private void RunTest()
{
obj.SomeEvent += handler;

obj.RaiseEvent(delegate { obj.SomeEvent -= handler; });
}

private void DoSomething(object sender, EventArgs e)
{
Console.WriteLine("foo");
}
}
}
//////////////////////////// Class1.cs
/////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpLib
{
public class Class1
{
public event EventHandler SomeEvent;

public delegate void Callback();

public Class1()
{
}

public void RaiseEvent(Callback callback)
{
EventHandler temp = SomeEvent;

callback();

if (temp != null)
{
temp(this, EventArgs.Empty);
}
}
}
}


Oct 28 '06 #1
Share this Question
Share on Google+
9 Replies


P: n/a
"CuriousGeorge" <***@***.comwrote in message
news:Ot****************@TK2MSFTNGP03.phx.gbl...
Can someone explain why this code DOES NOT raise a null reference
exception?
Because no nulls are referenced?

If you have reason to believe that a null reference exception should occur,
then the least you could do is explain in your post:
a) why it is you want to execute code like this, and
b) why and where it is that you think a null reference exception would
occur

Pete
Oct 28 '06 #2

P: n/a
Hi,

Where do you expect a null reference exception to be raised?

This line is checking to make sure temp is not null before it's used in
Class1:

if (temp != null)

--
Dave Sexton

"CuriousGeorge" <***@***.comwrote in message
news:Ot****************@TK2MSFTNGP03.phx.gbl...
Can someone explain why this code DOES NOT raise a null reference exception?
//////////////////////////// Program.cs
/////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Text;
using CSharpLib;

namespace CSharpConsole
{
class Program
{
private EventHandler handler;
private Class1 obj;

static void Main(string[] args)
{
(new Program()).RunTest();
Console.ReadKey(true);
}

private Program()
{
obj = new Class1();
handler = new EventHandler(DoSomething);
}

private void RunTest()
{
obj.SomeEvent += handler;

obj.RaiseEvent(delegate { obj.SomeEvent -= handler; });
}

private void DoSomething(object sender, EventArgs e)
{
Console.WriteLine("foo");
}
}
}
//////////////////////////// Class1.cs
/////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpLib
{
public class Class1
{
public event EventHandler SomeEvent;

public delegate void Callback();

public Class1()
{
}

public void RaiseEvent(Callback callback)
{
EventHandler temp = SomeEvent;

callback();

if (temp != null)
{
temp(this, EventArgs.Empty);
}
}
}
}


Oct 28 '06 #3

P: n/a
Because no nulls are referenced?
Hmmm. OK.

a) "Code like this" is the preferred way to raise events in a thread safe
manor - you should be doing it yourself.

b) Events are basically delegates, System.Delegate is a reference type,
there's nothing special about it, it overloads += and -= but other than that
its a pretty vanilla type.

As we know and event with no subscribers is actually a null-reference hence
the check for nullility before invoking the subscribed handlers.

If you in-line the delegate call in my original post you get something like
the following (compiled in Outlook Express)

public void RaiseEvent(Callback callback)
{
EventHandler temp = SomeEvent;

SomeEvent -= theOnlyRegisteredEventHandler
//SomeEvent is now null

if (temp != null)
{
temp(this, EventArgs.Empty);
}
}

The code in my original post draws the understanding reader to the
conclusion that this particular reference type is not obeying standard
reference type semantics. I thought it was pretty self-explanatory.
"Peter Duniho" <Np*********@NnOwSlPiAnMk.comwrote in message
news:12*************@corp.supernews.com...
"CuriousGeorge" <***@***.comwrote in message
news:Ot****************@TK2MSFTNGP03.phx.gbl...
>Can someone explain why this code DOES NOT raise a null reference
exception?

Because no nulls are referenced?

If you have reason to believe that a null reference exception should
occur, then the least you could do is explain in your post:
a) why it is you want to execute code like this, and
b) why and where it is that you think a null reference exception would
occur

Pete

Oct 28 '06 #4

P: n/a
Hi,
a) "Code like this" is the preferred way to raise events in a thread safe
manor - you should be doing it yourself.
I don't see anything particularly thread safe about your code, are you
talking about checking for null that makes this thread safe? It is safe for
a single thread but once you have multiple threads the really only safe way
is to make sure you have locks in place around adding/removing delegates to
your event and the invocation of that event i.e. the following is an example
of how two threads accessing the same event can cause a null reference
exception:

using System.Threading;

namespace CSharpConsole
{
class Program
{
static void Main(string[] args)
{
TestClass t = new TestClass();
t.Run();
}
}

class TestClass
{
private delegate void MyEventHandler();
private event MyEventHandler MyEvent;

public TestClass()
{
}

public void Run()
{
Thread threadA = new Thread(new ThreadStart(ThreadADoSomething));

//Add listener to the event
MyEvent += new MyEventHandler(DoSomething);

if (MyEvent != null)
{
//got in here, let the other thread run and wait until it
//completes
threadA.Start();
threadA.Join();

//whoops now this is null
MyEvent();
}
}

void ThreadADoSomething()
{
//removes the only listener
MyEvent -= new MyEventHandler(DoSomething);
}

void DoSomething()
{
}
}
}
b) Events are basically delegates, System.Delegate is a reference type,
there's nothing special about it, it overloads += and -= but other than that
its a pretty vanilla type.
There is a little bit more to this type, hence your posting :-) If you look
at the documentation for the delegate type you will see: "Delegates are
immutable; once created, the invocation list of a delegate does not change."
Hence the reason why when you assign you event (which is really just
syntactic sugar around delgates) to your other delegate type really you have
just made a new copy of that delegate. The contents of the
multicastdelegates invocation list inside the temp variable will therefore
not change when you update the other multicastdelegate through the SomeEvent
enty point.

The code in my original post draws the understanding reader to the
conclusion that this particular reference type is not obeying standard
reference type semantics. I thought it was pretty self-explanatory.
Why not just ask the question you want to ask :-)
--
http://www.markdawson.org
Posted from Windows Vista RC1

"CuriousGeorge" wrote:
Because no nulls are referenced?
Hmmm. OK.

a) "Code like this" is the preferred way to raise events in a thread safe
manor - you should be doing it yourself.

b) Events are basically delegates, System.Delegate is a reference type,
there's nothing special about it, it overloads += and -= but other than that
its a pretty vanilla type.

As we know and event with no subscribers is actually a null-reference hence
the check for nullility before invoking the subscribed handlers.

If you in-line the delegate call in my original post you get something like
the following (compiled in Outlook Express)

public void RaiseEvent(Callback callback)
{
EventHandler temp = SomeEvent;

SomeEvent -= theOnlyRegisteredEventHandler
//SomeEvent is now null

if (temp != null)
{
temp(this, EventArgs.Empty);
}
}

The code in my original post draws the understanding reader to the
conclusion that this particular reference type is not obeying standard
reference type semantics. I thought it was pretty self-explanatory.
"Peter Duniho" <Np*********@NnOwSlPiAnMk.comwrote in message
news:12*************@corp.supernews.com...
"CuriousGeorge" <***@***.comwrote in message
news:Ot****************@TK2MSFTNGP03.phx.gbl...
Can someone explain why this code DOES NOT raise a null reference
exception?
Because no nulls are referenced?

If you have reason to believe that a null reference exception should
occur, then the least you could do is explain in your post:
a) why it is you want to execute code like this, and
b) why and where it is that you think a null reference exception would
occur

Pete


Oct 28 '06 #5

P: n/a
"Delegates are immutable; once created, the invocation list of a delegate
does not change."

Word. Thanks Mark, I hadn't seen that bit of doco. The thing is, I did skim
the (managed) rotor source for delegates and saw nothing that suggested
immutability in the implementation. Forgive my ignorance but where in the IL
can I see this? (is it a type decorator)

"Why not just ask the question you want to ask :-)"

Quite simply, I don't have the expertise to express myself very well.

Case in point, immutability, I see it, I hear it, it is said by the oracle,
MSDN, however, the upshot is (to all intents and purposes) that the
assignment operator behaves like it is overloaded such that it works like a
value type. I have taken this as gospel for years with the System.String
class but now I see it here and it is making my head spin!!

Words fail me.

Please help :)
"Mark R. Dawson" <Ma*********@discussions.microsoft.comwrote in message
news:A1**********************************@microsof t.com...
Hi,
>a) "Code like this" is the preferred way to raise events in a thread safe
manor - you should be doing it yourself.

I don't see anything particularly thread safe about your code, are you
talking about checking for null that makes this thread safe? It is safe
for
a single thread but once you have multiple threads the really only safe
way
is to make sure you have locks in place around adding/removing delegates
to
your event and the invocation of that event i.e. the following is an
example
of how two threads accessing the same event can cause a null reference
exception:

using System.Threading;

namespace CSharpConsole
{
class Program
{
static void Main(string[] args)
{
TestClass t = new TestClass();
t.Run();
}
}

class TestClass
{
private delegate void MyEventHandler();
private event MyEventHandler MyEvent;

public TestClass()
{
}

public void Run()
{
Thread threadA = new Thread(new
ThreadStart(ThreadADoSomething));

//Add listener to the event
MyEvent += new MyEventHandler(DoSomething);

if (MyEvent != null)
{
//got in here, let the other thread run and wait until it
//completes
threadA.Start();
threadA.Join();

//whoops now this is null
MyEvent();
}
}

void ThreadADoSomething()
{
//removes the only listener
MyEvent -= new MyEventHandler(DoSomething);
}

void DoSomething()
{
}
}
}
>b) Events are basically delegates, System.Delegate is a reference type,
there's nothing special about it, it overloads += and -= but other than
that
its a pretty vanilla type.

There is a little bit more to this type, hence your posting :-) If you
look
at the documentation for the delegate type you will see: "Delegates are
immutable; once created, the invocation list of a delegate does not
change."
Hence the reason why when you assign you event (which is really just
syntactic sugar around delgates) to your other delegate type really you
have
just made a new copy of that delegate. The contents of the
multicastdelegates invocation list inside the temp variable will therefore
not change when you update the other multicastdelegate through the
SomeEvent
enty point.

>The code in my original post draws the understanding reader to the
conclusion that this particular reference type is not obeying standard
reference type semantics. I thought it was pretty self-explanatory.

Why not just ask the question you want to ask :-)
--
http://www.markdawson.org
Posted from Windows Vista RC1

"CuriousGeorge" wrote:
Because no nulls are referenced?
Hmmm. OK.

a) "Code like this" is the preferred way to raise events in a thread safe
manor - you should be doing it yourself.

b) Events are basically delegates, System.Delegate is a reference type,
there's nothing special about it, it overloads += and -= but other than
that
its a pretty vanilla type.

As we know and event with no subscribers is actually a null-reference
hence
the check for nullility before invoking the subscribed handlers.

If you in-line the delegate call in my original post you get something
like
the following (compiled in Outlook Express)

public void RaiseEvent(Callback callback)
{
EventHandler temp = SomeEvent;

SomeEvent -= theOnlyRegisteredEventHandler
//SomeEvent is now null

if (temp != null)
{
temp(this, EventArgs.Empty);
}
}

The code in my original post draws the understanding reader to the
conclusion that this particular reference type is not obeying standard
reference type semantics. I thought it was pretty self-explanatory.
"Peter Duniho" <Np*********@NnOwSlPiAnMk.comwrote in message
news:12*************@corp.supernews.com...
"CuriousGeorge" <***@***.comwrote in message
news:Ot****************@TK2MSFTNGP03.phx.gbl...
Can someone explain why this code DOES NOT raise a null reference
exception?

Because no nulls are referenced?

If you have reason to believe that a null reference exception should
occur, then the least you could do is explain in your post:
a) why it is you want to execute code like this, and
b) why and where it is that you think a null reference exception
would
occur

Pete



Oct 28 '06 #6

P: n/a
"CuriousGeorge" <***@***.comwrote in message
news:%2******************@TK2MSFTNGP02.phx.gbl...
a) "Code like this" is the preferred way to raise events in a thread safe
manor - you should be doing it yourself.
I disagree. As Mark points out, there's nothing thread-safe about the code
that you posted.
[...]
The code in my original post draws the understanding reader to the
conclusion that this particular reference type is not obeying standard
reference type semantics. I thought it was pretty self-explanatory.
Well, hopefully at this point, given the responses you've seen, it should be
clear to you that it was NOT "pretty self-explanatory".

On the bright side, it looks like you did finally get the answer to your
question. :)

Pete
Oct 28 '06 #7

P: n/a
CuriousGeorge <***@***.comwrote:
Because no nulls are referenced?
Hmmm. OK.

a) "Code like this" is the preferred way to raise events in a thread safe
manor - you should be doing it yourself.
It's not, actually. Your code isn't genuinely thread-safe. See
http://www.pobox.com/~skeet/csharp/t...ckchoice.shtml for a
genuinely thread-safe pattern.
b) Events are basically delegates
No they're not. Events are pairs of methods. Field-like events declare
both an event and a delegate.
System.Delegate is a reference type,
there's nothing special about it, it overloads += and -= but other than that
its a pretty vanilla type.
Actually, it *doesn't* overload += and -=. Instead, the C# compiler
calls Delegate.Combine or Delegate.Remove, both of which are static
methods which can take null arguments.

See http://www.pobox.com/~skeet/csharp/events.html for more
information.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Oct 29 '06 #8

P: n/a
Peter Duniho <Np*********@NnOwSlPiAnMk.comwrote:
"CuriousGeorge" <***@***.comwrote in message
news:%2******************@TK2MSFTNGP02.phx.gbl...
a) "Code like this" is the preferred way to raise events in a thread safe
manor - you should be doing it yourself.

I disagree. As Mark points out, there's nothing thread-safe about the code
that you posted.
In fairness to the OP, I wouldn't go that far. It's not *quite* thread-
safe, but it's safer than the normal pattern of:

if (EventName != null)
{
EventName (...);
}

where "EventName" is a field-like event.

The problem with the above is that another thread could change the
value of myEventName to null between the check and the call. The OP's
code tries to avoid that by using a local variable. Now, there's some
debate as to the exact guarantees of behaviour of local variables in
the memory model, but it's at least a reasonable start. However, the
lack of locking and volatility means that if one thread adds a load of
event handlers, they may never be noticed by the thread raising the
event, which isn't generally what's wanted.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Oct 29 '06 #9

P: n/a
Jon, your arachsys stuff is, quite simply, GOLD

"Jon Skeet [C# MVP]" <sk***@pobox.comwrote in message
news:MP************************@msnews.microsoft.c om...
CuriousGeorge <***@***.comwrote:
Because no nulls are referenced?
Hmmm. OK.

a) "Code like this" is the preferred way to raise events in a thread safe
manor - you should be doing it yourself.

It's not, actually. Your code isn't genuinely thread-safe. See
http://www.pobox.com/~skeet/csharp/t...ckchoice.shtml for a
genuinely thread-safe pattern.
>b) Events are basically delegates

No they're not. Events are pairs of methods. Field-like events declare
both an event and a delegate.
>System.Delegate is a reference type,
there's nothing special about it, it overloads += and -= but other than
that
its a pretty vanilla type.

Actually, it *doesn't* overload += and -=. Instead, the C# compiler
calls Delegate.Combine or Delegate.Remove, both of which are static
methods which can take null arguments.

See http://www.pobox.com/~skeet/csharp/events.html for more
information.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Oct 29 '06 #10

This discussion thread is closed

Replies have been disabled for this discussion.