2013-10-17 24 views
4

使用C#.NET 4.0Dispose()是否曾创建对象的新实例?

我公司的应用程序使用资源锁定器来防止记录被同时编辑。我们使用数据库来存储锁的开始时间以及获取锁的用户。这导致了下面的(奇怪?)落实处置上的资源锁,这恰好是从析构函数叫做:

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()期间创建新对象时是否有任何可能的副作用?

+0

你从哪里读过,在终结器中创建新对象不安全? –

+5

这将是非常酷,如果所以让我们大胆的线我们有问题 – Jonesopolis

+0

你已经在那个时间点处理dbio2吗?这可能是它不工作的原因。 – Servy

回答

1

这恰好从析构函数

这才是真正的问题被调用。你不能假定* dbio2对象本身并没有完成,在.NET中最终顺序不是确定性的,结果看起来很像你描述的,数据库提供者使用的内部句柄将被释放,所以“句柄没有初始化“,或者dbio2对象已经处理完毕

这很可能在程序退出时出错,当终止线程2秒超时时,质数据库操作可以很容易地采取更多。

你根本无法依靠终结为你做这个。你必须检查处置参数和不是如果为false,则调用dbio2.ExecuteScalar()方法。这可能也终结了析构函数的有用性。

1

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

进行数据库调用有点涉及到个人;我不希望在Dispose方法中调用如此昂贵和容易出错的活动,但这更多的是一种约定/期望。这个系统不会有问题。

+1

在其推荐的实现中进行处理实际上并不像其他任何方法一样 - 其中一半(在disposing == false之下)在终结器中执行,其中所有托管的子组件可能会消失/处置,或者至少表现得很奇怪由于在终结器线程上执行。另一半确实是正常的方法。 –

+1

@AlexeiLevenkov:实现'IDisposable'的公共类只有1%或更少应该有终结器(或C#“析构器”)。在绝大多数情况下,定稿逻辑应限于私人持有的类别实例(其目的集中在最终定稿阶段)(例如'SafeHandle')。推荐的'IDisposable'模式在当时似乎是个好主意,但将类管理与非管理资源结合在一起通常是一个坏主意。类的终结代码通常应该类似于托管清理(错误处理除外),否则不存在。 – supercat

1

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

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

相关问题