Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Local event listener called even though object failed to be constructed

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);
    }
}
like image 959
Siy Williams Avatar asked Aug 02 '11 13:08

Siy Williams


1 Answers

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.

like image 74
BoltClock Avatar answered Oct 02 '22 21:10

BoltClock