7

我调用 SQL Server 的方法返回 aDataReader但由于我需要做的 - 即返回DataReader到驻留在页面代码隐藏中的调用方法 - 我无法关闭调用 SQL 的方法的类中的连接服务器。因此,我没有 finally 或 using 块。

配置资源以使类实现的正确方法是IDisposable什么?或者,我应该从调用者显式处理非托管资源(类级字段)吗?

编辑:我将数据读取器发回,因为我需要将数据读取器中的特定数据绑定到 listitem 控件,所以在调用类(代码隐藏页面)中,我这样做:

 new ListItem(datareader["dc"]); (along those lines).
4

7 回答 7

7

我会说是的,实施IDisposable. 据我所知,使用它的主要原因之一是当你不能足够信任对象的用户来自己正确地完成它时。这似乎是一个主要的候选人。

话虽如此,但您的架构存在问题。为什么您想将DataReader自身发送到页面而不是通过返回必要的内容来调用方法为您执行此操作(包括相关的清理)?如果有必要让实际的读者进入页面,那就这样吧。

于 2010-05-19T16:58:20.103 回答
4

在您的阅读器类中将数据库连接作为成员变量并让您的阅读器类实现 IDisposable 对我来说似乎很好。

但是,您可能会考虑让您的方法返回 IEnumerable 并使用yield return语句来遍历数据读取器。这样,您可以返回结果并仍然从您的方法中进行清理。

这是我的意思的粗略草图:

public IEnumerable<Person> ReadPeople(string name)
{
    using (var reader = OpenReader(...))
    {
        // loop through the reader and create Person objects
        for ...
        {
            var person = new Person();
            ...
            yield return person;
        }
    }
}
于 2010-05-19T17:01:10.267 回答
3

是的,如果自定义类包含返回到较低层时打开的 DataReader,则应该在自定义类上实现 IDisposable。

这是返回某些东西时公认的模式,需要清理。

于 2010-05-19T16:59:13.033 回答
3

首先,传递DataReader可能不是你真正想要做的,但我会假设它是。

处理此问题的正确方法是传回一个复合类型,该类型封装或公开DataReader并保持连接,然后IDisposable在该类型上实现。处理该类型时,请同时处理阅读器和连接。

public class YourClass : IDisposable
{
    private IDbConnection connection;
    private IDataReader reader;

    public IDataReader Reader { get { return reader; } }

    public YourClass(IDbConnection connection, IDataReader reader)
    {
        this.connection = connection;
        this.reader = reader;
    }

    public void Dispose()
    {
        reader.Dispose();
        connection.Dispose();
    }
}
于 2010-05-19T17:00:40.007 回答
2

你的班

class MyClass : IDisposable
{
  protected List<DataReader> _readers = new List<DataReader>();
  public DataReader MyFunc()
  {
      ///... code to do stuff

      _readers.Add(myReader);
      return myReader;
  }
  private void Dispose()
  {
      for (int i = _readers.Count - 1; i >= 0; i--)
      {
          DataReader dr = _reader.Remove(i);
          dr.Dispose();
      }
      _readers = null;

      // Dispose / Close Connection
  }
}

然后在你的课外

public void FunctionThatUsesMyClass()
{
   using(MyClass c = new MyClass())
   {
       DataReader dr = c.MyFunc();
   }
}

当块退出时,所有读取器和MyClass实例都会被清理。using

于 2010-05-19T17:00:01.970 回答
2

我不会返回任何东西。相反,我会通过一个代表。

例如:

void FetchMeSomeReader(Action<IDataReader> useReader)
{
    using(var reader = WhateverYouDoToMakeTheReader())
        useReader(reader);
}

然后在你的调用类中:

void Whatever()
{
   FetchMeSomeReader(SetFields);
}

void SetFields(IDataReader reader)
{
   MyListItem = new ListItem(datareader["dc"]);
}
于 2010-05-19T21:18:20.047 回答
1

一般规则是,IDisposable如果您的类直接持有非托管资源或持有对另一个IDisposable对象的引用,则应实现该类。如果您的类创建了IDataReader一种方法,但从未持有该引用,那么您的类将不需要IDisposable按照规则实现(除非它恰好保留了IDisposable在该方法中IDataReader创建的方法)。

IDataReader您需要问自己的真正问题是,即使您的班级已将其交付给调用者,是否真的应该坚持这一点。就个人而言,我认为这是一个糟糕的设计,因为它模糊了所有权的界限。在这种情况下,谁真正拥有IDisposable?谁对它的一生负责?以IDbCommand上课为例。他们创建IDataReader实例并将它们返回给调用者,但免除自己的所有权。这使得 API 变得干净,并且在这种情况下生命周期管理的责任是明确的。

无论所有权问题如何,您的具体情况都需要实施 IDisposable;不是因为你的类碰巧创建并返回了一个IDataReader实例,而是因为它听起来像是一个IDbConnection对象。

于 2010-05-19T17:33:58.520 回答