即使对象构造失败,也会调用本地事件侦听器
在对象“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);
}
}
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);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
虽然从构造函数抛出异常意味着实例可能最终处于不完整状态,但这样做并不能阻止实例本身被创建并存储在内存中(因为这种情况发生在其构造函数之前)称为)。
此外,在引发异常时事件处理程序已经被绑定,因此引发事件将导致处理程序被调用。
为了快速说明第一点,如果您为
Listener
提供了一个要在其构造函数中初始化的字段,然后尝试在抛出异常后初始化它(这显然是行不通的):然后尝试在其
OnSomethingChanged
处理程序中访问它:无论您如何调用
new Listener(...)
,输出都只是因为侦听器没有机会初始化它的 foo 字段。虽然它没有完全初始化,但就分配而言它仍然是一个完整的对象。
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):And then tried to access it in its
OnSomethingChanged
handler:Regardless of how you call
new Listener(...)
, the output would besimply 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.