C# “我怎么能?”;连锁店;可识别对象

C# “我怎么能?”;连锁店;可识别对象,c#,idisposable,C#,Idisposable,我使用常规的Sql*对象来查询我的数据库: // conn is a SqlConnection // transaction is a SqlTransaction using(var cmd = new SqlCommand(someSelectQuery, conn, transaction)) using(var reader = cmd.ExecuteReader()) { ... } 我将编写一个包装器,它将newsqlcomman

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

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

    using(var cmd = new SqlCommand(someSelectQuery, conn, transaction))
    using(var reader = cmd.ExecuteReader())
    {
        ...
    }
我将编写一个包装器,它将
newsqlcommand()
cmd.ExecuteReader()
打包在一起:

     using(var someNewReader = GetSelectReader(someSelectQuery, conn, transaction))
     {
        ...
     }
问题是:这个“someNewReader”对象(或struct?)应该:

  • 以某种方式发布与
    SqlDataReader
  • 有一个
    .Dispose()
    方法,该方法同时处理底层的
    SqlCommand
    SqlDataReader

  • 我尝试的

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

  • 公开
    SqlDataReader的方法
  • 重新实现处置两个对象的
    .Dispose()
    方法(按正确顺序)

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

    问题

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

    • 输入:
      obj
      一次性的
    • 输入:
      parent
      一次性的
    • 输出仍然有一个
      obj
      (至少具有相同的公共接口),但它正确地调用
      obj.Dispose()
      ,然后在Dispose时调用
      parent.Dispose()

    我认为没有一种简单的方法可以制作一个包装来满足您的第一个条件: 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;
            }
        }
    }
    
    此选项提供了强大的键入功能。这和你现在的情况很相似。我不认为它非常优雅:但是,用工厂方法或只是嵌套<代码>使用< /代码>看起来更为常见。

    请注意,C#8包含一些使用
    的,现在您可以在声明中指定它:

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

    在正确的maner中重新实现.Dispose()会增加容易出错的编码开销
    -当您告诉VS在您的对象上实现
    IDisposable
    时,只需让它为您生成样板文件,然后填充空白即可。仔细检查您的选择是否符合要求。在你的例子中,如果你的类是密封的,它是第一个选项,如果不是,它是没有终结器的第二个选项。作为旁注,有些东西不需要你处理它们,就像它们被传递到一个对象中一样,对象负责处理它们。这通常发生在传递streams etcI将生成两个方法并将execute reader调用作为委托推送到另一个(处理命令和连接)时。它为您提供了所需的方法,但不能“链接IDisposable”。
    using var cmd = new SqlCommand(someSelectQuery, conn, transaction);
    using var reader = cmd.ExecuteReader();