在实现IDisposable时处理构造函数中的异常

时间:2011-12-15 21:17:51

标签: c# idisposable

我读过如果我的类有一个本身就是IDisposable的成员变量,我需要实现IDisposable。好吧,我正在实现IDisposable接口,因为我的类包含一个数据库对象(下面是db类成员),它本身就是IDisposable:

    public class CommissionModel : IDisposable
    {
        protected PetaPoco.Database db;

        public CommissionModel()
        {
            string connectionString = "Server=localhost;...";

            // The line below may throw an exception (!!!)
            db = new PetaPoco.Database(connectionString, "mysql");              
        }

        // Automatically close database connection
        public void Dispose()
        {
            if (db != null)
                db.Dispose();

            db = null;
        }

        public void InsertRecord(Record somerecord)
        {
            // ...
            db.Insert(somerecord);
        }

问题是db成员的实例化在某些情况下可能会失败。 在构造函数中抛出异常并且未创建数据库对象时,我该怎么办?我应该重新抛出异常,还是检查InsertRecord方法中是否db != null

5 个答案:

答案 0 :(得分:3)

如果构造函数抛出异常,代码的使用者将永远不会收到对类实例的引用。最终将收集部分初始化的类内存,并且不会调用Dispose。

我通常建议将可能因外部环境而失败的操作(并非由于误操作,如无效的参数值)移动到单独的方法,例如" Connect"。

答案 1 :(得分:2)

理想情况下,您不应该在构造函数中执行任何“工作”。构造函数中的工作应该真正链接对象引用。这将允许您通过挂钩模拟类来对此类进行单元测试。

请改为尝试:

public CommissionModel(PetaPoco.Database db) {
    this.db = db;
}

答案 2 :(得分:2)

看起来你不需要做任何事情。

如果您的施工过程中断,则不会创建资源。在大多数使用场景中(这在您的类之外),将永远不会调用Dispose()。但即使是这样,你的if (db != null)代码就足够了。

一些小问题:

  • Dispose()中的db = null;毫无意义。
  • InsertRecord()应首先检查if (db != null)

答案 3 :(得分:1)

您应该只捕获可以处理的异常。从代码中可以看出,初始化变量db可能表明与数据库服务器通信存在问题。

我的建议是保持代码不变,并在中心位置处理异常,例如Global.asax中的Application_Error事件或初始化CommissionModel的事件。

答案 4 :(得分:1)

如果构造函数获取资源的时间与返回应用程序的时间之间无法发生异常(*),则无需担心。

如果可能在这种情况下发生异常,我建议采用如下模式:

 public class CommissionModel : IDisposable
    {
        protected PetaPoco.Database db;
        protected OtherResourceType resource2;

        public CommissionModel()
        {
            Boolean ok;
            string connectionString = "Server=localhost;...";

            ok = false;
            try
            {
                // Either of the next two lines may throw an exception
                db = new PetaPoco.Database(connectionString, "mysql");
                resource2 = new OtherResourceType();
                // Once we make it this far, we should be successful
                ok = true;
            }
            finally
            {
                if (!ok)
                  Dispose();
            }
        }

        // Automatically close database connection
        public void Dispose()
        {
            Zap(ref db);  // Method to Dispose and null out, only if not null
            Zap(ref resource2);
        }
    }

(*)如果某些意味着讨厌的食人魔在你的代码上调用Tread.Abort,几乎总是可能发生ThreadAbortException,但在任何情况下都没有什么可以做的。