使用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()期间是否有任何可能的副作用伴随创建新对象?
答案 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()方法。这可能也会结束析构函数的用处。