开发者

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开发者_如何学Python 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜