【问题标题】:How can I "chain" IDisposable objects如何“链接” IDisposable 对象
【发布时间】:2019-10-10 22:52:52
【问题描述】:

我使用常规的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”对象(或结构?)应该:

  1. 不知何故发布了与SqlDataReader相同的方法
  2. 有一个.Dispose() 方法,它处理底层SqlCommandSqlDataReader

我尝试了什么

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

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

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

问题

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

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

【问题讨论】:

  • Re-implementing .Dispose() in a correct maner adds a coding overhead which is error prone - 当您告诉 VS 在您的对象上实现 IDisposable 时,只需让 VS 为您生成样板,然后填写空白。仔细检查您对stackoverflow.com/a/898867/11683 的选择。在你的情况下,如果你的类是密封的,它是第一个选项,如果不是,第二个没有终结器。
  • 作为旁注,有些事情不需要您处理它们,就好像它们被传递到一个对象中一样,该对象负责处理它们。这通常在传递流等时发生
  • 我将创建两个方法并将执行阅读器调用推送到另一个(处理命令和连接)作为委托。它为您提供了您想要的方法,但不会“链接 IDisposable”。

标签: c# idisposable


【解决方案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();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 2010-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-16
    • 2010-09-20
    相关资源
    最近更新 更多