Friday, April 11, 2008

 

Calling an instance method from outside the class before the constructor completes

If I asked the question "Can you call an instance method on an object before the constructor completes?", most programmers would think about it for awhile and then likely conclude "Sure, I do it all the time. It is very common to have the constructor itself call an instance method."


class MyClass
{
MyClass()
{
MyMethod();
}

void MyMethod()
{
}
}
 

But, what if I change the question a bit: "Can you call an instance method on an object from outside the class before the constructor completes?". What would your answer be then? I am guessing that most programmers may assume that cannot happen. If you assume that cannot happen, then "you assume to much" (stealing a line from Padme).

I suspect many readers of this posting are now thinking "Huh?", "What?" and "How could you do that?". How do you call a method from outside the class before the constructor has completed? In the typical usage, the constructor would always complete before calling a method from outside the class:


SomeClass someClass = new SomeClass();
someClass.SomeMethod();
 

But, things are not always typical, especially as programs move more asynchronous in nature. Although it is probably not desirable for code outside of a class to call an instance method on an object before its constructor has completed, there is a very really danger that this can happen! Consider, for example, the following two classes.



public class Class1
{
private object _someObject;

public Class1()
{
Class2 class2 = Class2.Instance;
class2.SomeEvent += new EventHandler(Class2_SomeEvent);
class2.Go();

System.Threading.Thread.Sleep(5000);
_someObject = new object();
}

private void Class2_SomeEvent(
object sender,
EventArgs e)
{
Type type = _someObject.GetType();
}
}

public class Class2
{
private static Class2 _instance = new Class2();

public event EventHandler SomeEvent;

private Class2()
{
}

public static Class2 Instance
{
get
{
return _instance;
}
}

public void Go()
{
MethodInvoker del = new MethodInvoker(DoSomething);
del.BeginInvoke(new AsyncCallback(DoSomething_Completed), null);
}

private void DoSomething()
{
}

private void DoSomething_Completed(IAsyncResult ar)
{
OnSomeEvent();
}

private void OnSomeEvent()
{
if (SomeEvent == null)
{
return;
}

try
{
SomeEvent(this, new EventArgs() );
}
catch(NullReferenceException ex)
{
Debug.Assert(false, "The Class1 constructor has not completed yet!");
}
}
}
 

In this example, Class2 is a very simple singleton that provides a Go() method, and a SomeEvent event. The constructor for Class1 registers for the event. And, for simplicity of this example, the Class1 constructor is also calling the Go() method, but it could potentially be called by other code outside of both Class1 and Class2. Also, note that I have intentionally added a Sleep() to the example to ensure that you see the problem.

So, there you have it. If you run the code above, you will get an assert because _someObject is null when the Class1.Class2_SomeEvent() event handler is called even though _someObject is explicitly set in the Class1 constructor.

If, from within a class constructor, you register for event handlers or pass delegates into asynchronous methods as callbacks, there exists a possibility that your event handler or callback may get invoked before the constructor has completed.

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]