In the constructor of an object, Listener
, we take an argument and subscribe to one of its events. If an exception is thrown within the constructor after the event is subscribed the OnSomethingChanged()
method is still called when the event is raised - even through the object was not successfully constructed and, as far as I'm aware, no instance exists.
Now I can fix this by obviously re-factoring the design slightly, however I'm more interested in why an instance method is called even though the constructor did not complete successfully? If the method uses any local variables that have not been initialised before the exception then obviously it goes BOOM!
class Program
{
static void Main(string[] args)
{
Input input = new Input();
try
{
new Listener(input);
}
catch (InvalidOperationException)
{
// swallow
}
input.ChangeSomething(); // prints "Something changed!"
}
}
public class Listener
{
public Listener(Input input)
{
input.SomethingChanged += OnSomethingChanged; // subscibe
throw new InvalidOperationException(); // do not let constructor succeed
}
void OnSomethingChanged(object sender, EventArgs e)
{
Console.WriteLine("Something changed!");
}
}
public class Input
{
public event EventHandler SomethingChanged;
public void ChangeSomething()
{
SomethingChanged(this, EventArgs.Empty);
}
}
While throwing an exception from a constructor means an instance may potentially end up in an incomplete state, doing so doesn't stop the instance itself from being created and stored in memory (as that happens before its constructor is called).
Furthermore, the event handler has already been bound by the time you throw the exception, so raising the event will cause the handler to be invoked.
To quickly illustrate the first point, if you gave Listener
a field to initialize in its constructor, then tried to initialize it after throwing the exception (which obviously isn't going to work):
string foo;
public Listener(Input input, string f)
{
input.SomethingChanged += OnSomethingChanged;
// Because this is thrown...
throw new InvalidOperationException();
// ... this never happens
foo = f;
}
And then tried to access it in its OnSomethingChanged
handler:
void OnSomethingChanged(object sender, EventArgs e)
{
Console.WriteLine("Listener.foo = " + foo);
}
Regardless of how you call new Listener(...)
, the output would be
Listener.foo =
simply because the listener didn't get a chance to initialize its foo
field. Although it wasn't fully initialized, it's still a complete object in terms of allocation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With