解决问题"无法访问被处置对象".例外

hai*_*yyu 13 c# dispose exception winforms

在我当前的项目中有一个Form类,如下所示:

public partial class FormMain : Form
{

    System.Timers.Timer timer;
    Point previousLocation;
    double distance;

    public FormMain()
    {
        InitializeComponent();

        distance = 0;
        timer = new System.Timers.Timer(50);
        timer.AutoReset = true;
        timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
        timer.Start();
    }

    private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        if (previousLocation != null)
        {
            // some code

            UpdateDistanceLabel(distance);
            UpdateSpeedLabel(v);
        }

        previousLocation = Cursor.Position;
    }

    private void UpdateDistanceLabel(double newDistance)
    {
        if (!lblDistance.IsDisposed && !IsDisposed)
        {
            Invoke(new Action(() => lblDistance.Text = String.Format("Distance: {0} pixels", newDistance)));
        }
    }

    private void UpdateSpeedLabel(double newSpeed)
    {
        if (!lblSpeed.IsDisposed && !IsDisposed)
        {
            Invoke(new Action(() => lblSpeed.Text = String.Format("Speed: {0} pixels per second", newSpeed)));
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

如您所见,我正在使用System.Timers.Timer对象.我知道我可以使用System.Windows.Forms.Timer,但我对我仍然得到标题中显示的异常的原因很感兴趣.它会在UpdateDistanceLabel方法的Invoke调用中抛出.令我困惑的是它说"无法访问处置对象:FormMain",即使我正在检查它是否被处置.所以这不应该发生.我也尝试在FormClosing事件中处理timer对象以及重写Dispose(bool)并将其处理在那里,但遗憾的是两者都没有帮助.此外,异常并不总是被抛出,据说只有在程序退出时计时器发生时才会被抛出.它仍然发生了很多.

我已经看到有关于此的大量线索,但我已经尝试过那里发布的解决方案,其中大多数涉及检查IsDisposed属性 - 这对我不起作用.所以我想我做错了什么.

所以我的问题:为什么上面发布的代码会触发异常,即使我正在检查我访问的对象是否被处理?

sup*_*cat 10

有两种解决方法:要么吞下异常,并诅咒微软没有包括一个TryInvokeTryBeginInvoke方法,或者使用锁定,以确保不尝试做出Dispose的对象,而它在使用中,并没有尝试使用对象而Dispose为进行中.我认为吞下异常可能更好,但有些人对这些事情有内心反应,并且使用锁定可以避免首先发生异常.


Joe*_*Joe 9

一个问题是你在调用之前正在检查计时器线程Invoke.存在可能的竞争条件,其中可以在检查之后并且在执行调用的动作之前处理表单.

您应该在方法内部(在您的情况下为lambda表达式)中进行检查Invoke.

另一个可能的问题是您正在访问Cursor.Position计时器线程.我不确定这是否有效 - 我会在主线程上执行此操作.您的代码还包含注释//some code- 因此您可能会省略一些您还需要检查的代码.

总的来说,你可能会更好地使用System.Windows.Forms.Timer.


adr*_*fek 6

如果您有兴趣,这是我的异常解决方案:

private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            timer.Stop();
            Application.DoEvents();       
        }
Run Code Online (Sandbox Code Playgroud)

没有.DoEvents()的.Stop()是不够的,因为它会在不等待你的线程完成其工作的情况下处理对象.