C# 在构造函数引发异常时确保定义良好的对象状态
我有一个VisualStudio2008C#.NET2.0CF应用程序。我使用的是一个组件基础,从中派生出两个具体组件。应用程序首先尝试使用C# 在构造函数引发异常时确保定义良好的对象状态,c#,exception,constructor,c#-2.0,dispose,C#,Exception,Constructor,C# 2.0,Dispose,我有一个VisualStudio2008C#.NET2.0CF应用程序。我使用的是一个组件基础,从中派生出两个具体组件。应用程序首先尝试使用SomeDisposableComponent。它的构造函数引发异常,因为它需要一个不可用的功能。然后,应用程序尝试SomeOtherDisposableComponent。它的建设成功了 问题是,在引发异常之前,第一个组件的构造函数已经将自己添加到表单的组件容器中。因此,当表单被释放时,调用第一个组件的Dispose()成员,即使对象从未完全构造。这会导致
SomeDisposableComponent
。它的构造函数引发异常,因为它需要一个不可用的功能。然后,应用程序尝试SomeOtherDisposableComponent
。它的建设成功了
问题是,在引发异常之前,第一个组件的构造函数已经将自己添加到表单的组件容器中。因此,当表单被释放时,调用第一个组件的Dispose()
成员,即使对象从未完全构造。这会导致第二个组件的析构函数出现问题
如何确保当第一个组件在构造时引发异常时,删除对它的引用
public abstract class SomeDisposableComponentBase : Component
{
private System.ComponentModel.IContainer components;
private SomeInternalDisposable s_ = new SomeInternalDisposable();
protected SomeDisposableComponentBase()
{
Initializecomponent();
}
protected SomeDisposableComponentBase(IContainer container)
{
container.Add(this);
Initializecomponent();
}
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
protected abstract void Foo();
#region IDisposable Members
bool disposed_;
protected override void Dispose(bool disposing)
{
// called twice. the first time for the component that failed to initialize properly.
// the second for the one that was used.
if (!disposed_)
{
if (disposing && (components != null))
{
components.Dispose();
}
// on the second call, this throws an exception because it is already disposed.
s_.Close();
disposed_ = true;
}
base.Dispose(disposing);
}
#endregion
}
public SomeDisposableComponent : SomeDisposableComponentBase
{
public SomeDisposableComponent() : base()
{
}
public SomeDisposableComponent(IContainer container) : base(container)
{
// This will throw an exception if it requires a feature that isn't available.
SomeInitFunction();
}
protected override void Foo()
{
// Do something...
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}
public partial class my_form : Form
{
private SomeDisposableComponentBase d_;
public my_form()
{
InitializeComponent();
if (null == components)
components = new System.ComponentModel.Container();
try
{
// try the default component
d_ = new SomeDisposableComponent(components);
}
catch (System.Exception)
{
try
{
// the default component requires some feature that isn't available. Try a
// backup component.
d_ = new SomeOtherDisposableComponent(components);
}
catch (System.Exception e)
{
// display error to the user if no suitable component can be found.
}
}
}
/// exit button clicked
private void Exit_Click(object sender, EventArgs e)
{
this.Close();
}
/// from the my_form.designer.cs
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
// this function is executed as expected when the form is closed
components.Dispose();
}
base.Dispose(disposing);
}
}
谢谢,
保罗
编辑:删除未使用的代码
SomeDisposableComponentBase
中的容器令人困惑。这与问题无关,我应该早点删除它
public abstract class SomeDisposableComponentBase : Component
{
private SomeInternalDisposable s_ = new SomeInternalDisposable();
protected SomeDisposableComponentBase()
{
}
protected SomeDisposableComponentBase(IContainer container)
{
container.Add(this);
}
protected abstract void Foo();
#region IDisposable Members
bool disposed_;
protected override void Dispose(bool disposing)
{
// called twice. the first time for the component that failed to initialize properly.
// the second for the one that was used.
if (!disposed_)
{
if (disposing)
{
// on the second call, this throws an exception because it is already disposed.
s_.Close();
}
disposed_ = true;
}
base.Dispose(disposing);
}
#endregion
}
public SomeDisposableComponent : SomeDisposableComponentBase
{
public SomeDisposableComponent() : base()
{
}
public SomeDisposableComponent(IContainer container) : base(container)
{
// This will throw an exception if it requires a feature that isn't available.
SomeInitFunction();
}
protected override void Foo()
{
// Do something...
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}
public partial class my_form : Form
{
private SomeDisposableComponentBase d_;
public my_form()
{
InitializeComponent();
if (null == components)
components = new System.ComponentModel.Container();
try
{
// try the default component
d_ = new SomeDisposableComponent(components);
}
catch (System.Exception)
{
try
{
// the default component requires some feature that isn't available. Try a
// backup component.
d_ = new SomeOtherDisposableComponent(components);
}
catch (System.Exception e)
{
// display error to the user if no suitable component can be found.
}
}
}
/// exit button clicked
private void Exit_Click(object sender, EventArgs e)
{
this.Close();
}
/// from the my_form.designer.cs
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
// this function is executed as expected when the form is closed
components.Dispose();
}
base.Dispose(disposing);
}
}
为什么
container.Add(this)
出现在InitializeComponent()之前?根据定义,您正在向容器添加未初始化的组件;即使initialize方法没有失败,这也可能导致问题
只需切换顺序:
InitializeComponent();
container.Add(this);
这样,如果InitializeComponent
抛出,组件将永远不会将自己添加到容器中。不需要特别清理
我不确定您为什么需要该构造函数,但如果出于某种原因,它必须按照您已有的顺序(即InitializeComponent
方法取决于容器
…eek),则将异常处理放在构造函数本身中:
try
{
container.Add(this);
InitializeComponent();
}
catch (WhateverException)
{
container.Remove(this);
}
但是不要这样做,除非你真的必须这样做;最好不要将组件添加到容器中,直到它完全初始化并稳定。为什么container.add(这个)
出现在InitializeComponent()之前?根据定义,您正在向容器添加未初始化的组件;即使initialize方法没有失败,这也可能导致问题
只需切换顺序:
InitializeComponent();
container.Add(this);
这样,如果InitializeComponent
抛出,组件将永远不会将自己添加到容器中。不需要特别清理
我不确定您为什么需要该构造函数,但如果出于某种原因,它必须按照您已有的顺序(即InitializeComponent
方法取决于容器
…eek),则将异常处理放在构造函数本身中:
try
{
container.Add(this);
InitializeComponent();
}
catch (WhateverException)
{
container.Remove(this);
}
但是不要这样做,除非你真的必须这样做;在组件完全初始化并稳定之前,最好不要将其添加到容器中。表单应该负责向其组件集合中添加项,而不是要添加到集合中的组件。在添加的组件中执行此操作会使您很难对表单中发生的事情进行推理
// It is not obvious that the component is adding itself to the list
new SomeDisposableComponent(components);
// Instead, be explicit where it matters
Component someDisposableComponent = new SomeDisposableComponent(components);
this.components.Add(someDisposableComponent);
接下来,要解决构造函数问题,应该将逻辑移出构造函数。这通常是一个很好的实践——人们不希望构造函数有副作用,并且构造函数中的逻辑使得类很难测试。如果您想保证每次创建实例时都会发生一些事情,请创建一个工厂,使其成为获取类实例的唯一方法(将类的构造函数设置为内部,或使用其他技术):
最后,您应该考虑将逻辑从您的窗体中选择和创建组件,并进入通用组件工厂:
public class ComponentFactory {
// The input parameter can be whatever you need to choose the right component
public Component CreateInstance(object input) {
if (input == something) {
SomeDisposableComponent component = new SomeDisposableComponent();
component.SomeInitFunction();
return component;
}
else {
return new AnotherComponent();
}
}
}
表单应该负责向其组件集合中添加项,而不是要添加到集合中的组件。在添加的组件中执行此操作会使您很难对表单中发生的事情进行推理
// It is not obvious that the component is adding itself to the list
new SomeDisposableComponent(components);
// Instead, be explicit where it matters
Component someDisposableComponent = new SomeDisposableComponent(components);
this.components.Add(someDisposableComponent);
接下来,要解决构造函数问题,应该将逻辑移出构造函数。这通常是一个很好的实践——人们不希望构造函数有副作用,并且构造函数中的逻辑使得类很难测试。如果您想保证每次创建实例时都会发生一些事情,请创建一个工厂,使其成为获取类实例的唯一方法(将类的构造函数设置为内部,或使用其他技术):
最后,您应该考虑将逻辑从您的窗体中选择和创建组件,并进入通用组件工厂:
public class ComponentFactory {
// The input parameter can be whatever you need to choose the right component
public Component CreateInstance(object input) {
if (input == something) {
SomeDisposableComponent component = new SomeDisposableComponent();
component.SomeInitFunction();
return component;
}
else {
return new AnotherComponent();
}
}
}
该容器实际上总是空的,并且未使用。我已经从示例代码中消除了干扰。问题仍然存在。问题的容器是表单()中的容器,而不是SomeDisposableComponentBase
。该容器实际上始终为空且未使用。我已经从示例代码中消除了干扰。问题仍然存在。问题的容器是表单()中的容器,而不是SomeDisposableComponentBase
。