Delegate Invocation List contains COPIES of objects, NOT reference

G

Guest

I am trying to get an exception to occur and consequently found that when
adding a target method to a delegates invocation list, a copy of that object
is added instead of a reference to the object.

Here's what I'm trying to do... I am iterating through the invocation list
invoking each delegate manually. I want to eventually try to invoke a
delegate to an object that has been destroyed. I'm essentially looking to get
the exception "object not set to an instance of an object" or some kind of
exception.

I've created two classes to test with, one class has a delegate as follows:

public class classContainsDelegate{
public delegate void takesOneString(string message);
public takesOneString myStringEvent;

public void raiseEvent(string message){
if(myStringEvent != null){
foreach(takesOneString d in myStringEvent.GetInvocationList()){
d(message);
}//foreach
}//if
}//raiseevent
}//class


I have another class that is going to simply display the message, when
called by the above delegate:

public class classBeingCalledBack(string key){
private string key = "";

public classBeingCalledBack(string theKey){
key = theKey;
}//new method

public string Key{
get{return key;}
}//key property

public void theCallback(string message){
Console.WriteLine("theCallback received '{0}' by '{1}', message, key);
}//the callback
}//class


My test routine starts out like this:

classContainingDelegate d1 = new classContainingDelegate();
classBeingCalledBack o1 = new classBeingCalledBack("first instance");
classBeingCalledBack o2 = new classBeingCalledBack("second instance");

d1.myStringEvent += new
classContainingDelegate.takesOneString(o1.theCallBack);
d1.myStringEvent += new
classContainingDelegate.takesOneString(o2.theCallBack);

d1.raiseEvent("message #1");

When I run this test, the 2 callbacks are made to both objects and I see the
messages being displayed that the callbacks happened for objects keyed "first
instance" and "second instance".

Now I want to kill the "first instance" object altogether. I want to cause
an exception when the delegate goes to make a callback to an object that
shouldn't exist anymore. So I then add the following lines to my test routine:

o1 = null;
d1.raiseEvent("message #2");

When doing this, I still get both callbacks still. No exceptions being thrown!

Now, even if I go and change o1 like:
o1 = new classBeingCalledBack("first instance - version 2");

When the delegate is called for "o1", I would expect to see a message that
the callback occurred for "first instance - version 2", but instead I see
"first instance" which should have been overwritten.

I have tried forcing a garbage collection, no difference.

I have tried creating an Event instead of using a delegate (yeah I know its
the same thing, but the rules slightly differ between the two) and no
difference.

Anybody have any ideas? What am I doing wrong or not getting here?

Thanks in advance.
 
N

Nicholas Paldino [.NET/C# MVP]

N8,

The reason this happens is that when you create a delegate, the delegate
itself holds a reference to the object that the method is on. This way,
when you set the original reference to null, and do a GC, the object still
lives, because you are holding the delegate, which holds the object.

A copy of the object is not made, but rather, the original is just kept
alive because you are holding a reference (indirectly through the delegate)
to it.

Hope this helps.
 
G

Guest

Thanks Nicholas,

You are right! I completely forgot the rules there when doing this test. (I
blame my xmas vacation - lol).

Thanks again.
 
R

Richard Grimes [MVP]

Nicholas said:
N8,

The reason this happens is that when you create a delegate, the
delegate itself holds a reference to the object that the method is
on. This way, when you set the original reference to null, and do a
GC, the object still lives, because you are holding the delegate,
which holds the object.
A copy of the object is not made, but rather, the original is just
kept alive because you are holding a reference (indirectly through
the delegate) to it.

Indeed, But of course the delegate is serializable, if the objects that are
reference are also serializable. Which gives you the odd situation that you
can serialize a delegate, shutdown the app, re start the app, deserialize
the delegate and invoke it. Resurection of the objects!

Richard
 

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