4

使用 C#.NET 4.0

我公司的应用程序使用资源锁来防止同时编辑记录。我们使用数据库来存储锁的开始时间以及获得锁的用户。这导致了资源锁上 dispose 的以下(奇怪?)实现,它恰好是从析构函数中调用的:

protected virtual void Dispose(bool disposing)
        {
            lock (this)
            {
                if (lockid.HasValue)
                {
                    this.RefreshDataButtonAction = null;
                    this.ReadOnlyButtonAction = null;

                try
                {
                    **Dictionary<string, object> parameters = new Dictionary<string, object>();
                    parameters.Add("@lockID", lockid.Value);
                    parameters.Add("@readsToDelete", null);
                    Object returnObject = dbio2.ExecuteScalar("usp_DeleteResourceLockReads", parameters);**

                    lockid = null;
                }
                catch (Exception ex)
                {
                    Logger.WriteError("ResourceLockingController", "DeleteResourceLocks", ex);
                }
                finally
                {
                    ((IDisposable)_staleResourcesForm).Dispose();
                    _staleResourcesForm = null;
                }
            }
        }
    }

我担心粗体部分,因为我们一直在从数据库调用中记录奇怪的“句柄未初始化”异常。我在别处读到在 Finalize() 期间创建新对象是不安全的,但同样的规则是否适用于 dispose()?在 Dispose() 期间创建新对象是否有任何可能的副作用?

4

3 回答 3

1

Dispose只是一种方法,就像任何其他方法一样。有一些关于它应该/不应该做的事情的约定,但从系统的角度来看,在Dispose调用中创建对象并没有什么问题。

拨打 DB 电话对个人来说有点令人担忧。我不希望在方法中调用如此昂贵且容易出错的活动Dispose,但这更像是一种约定/期望。系统不会有这个问题。

于 2013-10-17T17:18:02.200 回答
1

是的,但除非创建的对象在方法的本地范围内,否则我不会这样做。IDisposable 是一个广告,表明该类有一些资源(通常是非托管资源),当不再使用对象时应该释放这些资源。如果您的 Dispose 正在被您的终结器调用(即您没有直接调用析构函数,而是等待 GC 执行它),则可能表明您应该更早地调用它。您永远不知道 C# 析构函数何时运行,因此您可能会不必要地占用该资源。这也可能表明您的类不需要实现 IDisposable。

在您的情况下,您正在使用我认为代表您的数据库连接的对象 dbio2。但是,由于这是从析构函数调用的,你怎么知道你的连接是否仍然有效?您可以在连接丢失一小时后进行析构。您应该尝试确保在您知道 dbio2 对象仍在范围内时调用此 Dispose。

于 2013-10-17T17:41:00.370 回答
1

恰好是从析构函数中调用的

这才是真正的问题。您不能假设 *dbio2" 对象本身尚未最终确定。最终确定顺序在 .NET 中不是确定性的。结果看起来很像您描述的那样,dbase 提供程序使用的内部句柄将被释放,因此“句柄未初始化”预计会出现异常。或者 dbio2 对象已被释放。

这在程序退出时特别容易出错。当终结器线程的 2 秒超时时,您也会遇到问题,dbase 操作很容易花费更多时间。

您根本不能依靠终结器为您执行此操作。您必须检查disposing参数并且当它为 false 时不要调用 dbio2.ExecuteScalar() 方法。这可能也结束了析构函数的用处。

于 2013-10-17T18:23:53.200 回答