Dispose()是否应该创建新的对象实例?

时间:2013-10-17 17:12:20

标签: c# dispose

使用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;
                }
            }
        }
    }

我担心粗体部分我们因为从数据库调用中记录了奇怪的“Handle is not initialized”异常。我在其他地方读到在Finalize()期间创建新对象是不安全的,但同样的规则是否适用于dispose()?在Dispose()期间是否有任何可能的副作用伴随创建新对象?

3 个答案:

答案 0 :(得分:1)

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

进行数据库通话对于个人而言有点令人担忧;我不希望在Dispose方法中调用这样昂贵且容易出错的活动,但这更像是一种约定/期望。系统不会有问题。

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

  

恰好从析构函数中调用

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

这在程序退出时尤其可能出错。当终结器线程的2秒超时时,你也会遇到问题,dbase操作可以轻松获得更多。

你根本无法依靠终结者为你做这件事。 必须检查 disposing 参数, not 在错误时调用dbio2.ExecuteScalar()方法。这可能也会结束析构函数的用处。