即使对象构建失败,本地事件监听器仍然被调用。

8
在对象的构造函数Listener中,我们使用一个参数并订阅其中一个事件。如果在订阅事件后构造函数内抛出异常,则当事件被触发时仍会调用OnSomethingChanged()方法 - 即使对象未成功构造,并且我所知道的没有任何实例存在。

现在我可以通过显然重新设计来解决这个问题,但我更感兴趣的是为什么即使构造函数未成功完成,仍会调用实例方法?如果该方法使用了任何未在异常之前初始化的本地变量,那么它显然就会失败!

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);
    }
}
1个回答

6

在构造函数中抛出异常意味着实例可能最终处于不完整的状态,但这并不能阻止实例本身被创建并存储在内存中(因为这发生在其构造函数被调用之前)。

此外,事件处理程序在抛出异常时已经被绑定,因此引发事件将导致处理程序被调用。

为了快速说明第一点,如果您在Listener的构造函数中添加一个要初始化的字段,并尝试在抛出异常后进行初始化(显然是行不通的):

    string foo;

    public Listener(Input input, string f)
    {
        input.SomethingChanged += OnSomethingChanged;

        // Because this is thrown...
        throw new InvalidOperationException();

        // ... this never happens
        foo = f;
    }

然后在其OnSomethingChanged处理程序中尝试访问它:

    void OnSomethingChanged(object sender, EventArgs e)
    {
        Console.WriteLine("Listener.foo = " + foo);
    }

无论您如何调用new Listener(...),输出结果都会是:

Listener.foo = 

这是因为监听器没有机会初始化其foo字段。虽然它未完全初始化,但在分配方面它仍然是一个完整的对象。


这确实非常有趣,不是我所预料的。我还发现这篇帖子非常有启发性。感谢您的回复。 - Siy Williams
不用谢。那篇帖子完全涉及另一个问题,但是有类似的前提存在。 - BoltClock

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接