在一个DataReader周围放置一个“using”语句会关闭它吗?

29

我通常这样编写我的DataReader代码:

try
{
    dr = cmd.ExecuteReader(CommandBehavior.SingleResult);
    while (dr.Read())
    {
        // Do stuff
    }
}
finally
{
    if (dr != null) { dr.Close(); }
}

使用using块替换tryfinally是否安全,这样可以围绕DataReader的创建?我想知道的原因是因为在所有我看到的Microsoft示例中,他们都对连接使用using,但总是显式调用Close()来关闭DataReader
以下是来自Retrieving Data Using a DataReader(ADO.NET)的示例:
static void HasRows(SqlConnection connection)
{
    using (connection)
    {
        SqlCommand command = new SqlCommand(
          "SELECT CategoryID, CategoryName FROM Categories;",
          connection);
        connection.Open();

        SqlDataReader reader = command.ExecuteReader();

        if (reader.HasRows)
        {
            while (reader.Read())
            {
                Console.WriteLine("{0}\t{1}", reader.GetInt32(0),
                    reader.GetString(1));
            }
        }
        else
        {
            Console.WriteLine("No rows found.");
        }
        reader.Close();
    }
}

2
顺便提一下,我看到的大多数微软示例要么过于复杂,要么没有充分利用可用的语言特性。部分原因是因为他们只是从以前的版本中滚动示例代码,并且只检查它是否仍在运行。我的猜测是,这些代码不是由他们最聪明的人维护的,因为代码从来不会聪明或高效。 - NotMe
1
从示例代码的质量来看,我怀疑已经有好几年了,这是由初级实习生编写和维护的,而不是那些设计了这些类的人。正是像微软平庸的.NET文档这样的东西,让我期待SO的新文档扩展 - user456814
2
@Cupcake 在我看来不是重复的问题。这个问题问的是 using 块是否会关闭数据读取器,而另一个问题则问是否需要关闭它。两者并不相同。 - Shadow The Spring Wizard
@ShadowWizard 这是一个很好的观点。 - user456814
4个回答

22

是的。using调用Dispose。在SqlDataReader上调用Dispose会将其关闭。

这是从Reflector中获得的SqlDataReader伪代码:

    public void Dispose()
    {
        this.Close();
    }

    public override void Close()
    {
        if( !IsClosed )
            CloseInternal(true);
    }

    private void CloseInternal(bool closeReader)
    {
        try
        {
            // Do some stuff to close the reader itself
        }
        catch(Exception ex)
        {
            this.Connection.Abort();
            throw;
        }

        if( this.Connection != null && CommandBehavior.CloseConnection == true )
        {
            this.Connection.Close();
        }
    }

4
通常情况下,using()调用Dispose(),而后者又依次调用close()
在DataReader的情况下,仅当设置了CommandBehavior.CloseConnection时才会调用Close(请参见本文中的评论)。
编辑:这篇文章提供了一些有趣的信息:

SqlDataReader上的Close()方法调用了一个InternalClose()方法,该方法不调用Dispose。请注意,之前我们说过正确的做法是让你的close调用dispose。为了使它更加混乱,Dispose()方法实际上调用了Close()方法,因此对于该对象,顺序被颠倒了。


1

与此示例不同的是,我的做法是使用 using 块来处理连接、命令和读取器。请注意,您可以堆叠嵌套的 using 块以降低缩进成本。

static void HasRows(SqlConnection connection)
{
    using (connection)
    using (SqlCommand command = new SqlCommand(
    "SELECT CategoryID, CategoryName FROM Categories;",
    connection))
    {
        connection.Open();
        using (SqlDataReader reader = command.ExecuteReader())
        {
            if (reader.HasRows)
            {
                while (reader.Read())
                {
                    Console.WriteLine("{0}\t{1}", reader.GetInt32(0),
                        reader.GetString(1));
                }
            }
            else
            {
                Console.WriteLine("No rows found.");
            }
            reader.Close();
        }   
    }
}

1
据我所记,如果在 Using 块中发生异常,则仍会调用对象的 Dispose 方法。我通常为所有可处理对象使用 Using 语句,而不使用 Try..Catch。
编辑:忘了说对于某些对象,调用 Dispose 将反过来调用该对象的 Close 方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接