如何“链接” IDisposable对象

时间:2019-10-10 22:52:52

标签: c# idisposable

我使用常规的Sql*对象来查询数据库:

// conn is a SqlConnection
// transaction is a SqlTransaction

    using(var cmd = new SqlCommand(someSelectQuery, conn, transaction))
    using(var reader = cmd.ExecuteReader())
    {
        ...
    }

我要写一个包装器,将new SqlCommand()cmd.ExecuteReader()包装在一起:

     using(var someNewReader = GetSelectReader(someSelectQuery, conn, transaction))
     {
        ...
     }

问题是:这个“ someNewReader”对象(或struct?)应该:

  1. 以某种方式发布与SqlDataReader相同的方法
  2. 具有一个.Dispose()方法,该方法同时处理了基础SqlCommandSqlDataReader

我尝试过的事情

我尝试创建一个包装类,其中包含两个字段,一个SqlCommand和一个SqlDataReader,以及:

  1. 公开了SqlDataReader
  2. 的方法
  3. 重新实现.Dispose()方法,该方法以正确的顺序处理两个对象

以正确的方式重新实现.Dispose()(尤其是:以正确的方式处理异常,并且仍然尝试.Dispose()每个人)会增加编码开销,该开销容易出错,但是会遵循每个.Dispose()链的精确smae结构。

问题

我想知道是否有一种机制可以将多个IDisposable对象“链接在一起”,这种东西可以让我描述一下:

  • 输入:objDisposable
  • 输入:parentDisposable
  • 输出仍然具有obj(至少具有相同的公共接口),但可以正确调用obj.Dispose(),然后调用parent.Dispose()

1 个答案:

答案 0 :(得分:-1)

我认为没有一种简单的方法可以使包装纸满足您的第一个条件: 1.以某种方式发布与SqlDataReader相同的方法

但是,您可以返回一个一次性容器并访问其项目,这样就无需实现包装程序:

class DisposableTupleContainer<D1, D2>: IDisposable
        where D1 : IDisposable
        where D2 : IDisposable
{
    private bool _disposed = false;
    private (D1, D2) _items;
    public D1 Item1 => _disposed ? throw new ObjectDisposedException("d1") : _items.Item1;
    public D2 Item2 => _disposed ? throw new ObjectDisposedException("d2") : _items.Item2;

    public DisposableTupleContainer(D1 d1, Func<D1,D2> d2) 
         // add try catch to destroy d1 if d2() throws and exception
         => _items = (d1, d2(d1)); 

    public void Dispose()
    {
        if (!_disposed)
        {
            // dispose d1, d2, handle exception
            _disposed = true;
        }
    }
}

d2是一个函子,以便您可以链接一次性物品的构造:

using(var container = new DisposableTupleContainer(
   new SqlCommand(someSelectQuery, conn, transaction), 
   c => c.ExecuteReader())) 
{
  //  container.Item2 is your reader
}

此选项使您可以进行强键入。这与您现在拥有的东西相似。不过,我认为它不是很优雅:),带有工厂方法的选项或仅嵌套using对我来说似乎更常见。

请注意,C#8包含improvementsusing,现在您可以在声明时指定它:

using var cmd = new SqlCommand(someSelectQuery, conn, transaction);
using var reader = cmd.ExecuteReader();