C# SQL如何正确处理Dispose?

5
我有以下代码来查询存储过程中的记录,但我担心我可能没有处理好需要处理的内容,或者在对象很快被垃圾收集器清除之前就进行了处理。
既然 SqlDataReader 在 try catch 块内部,那么我需要释放它吗?
我需要运行 cmd.Dispose 和 cmd.Connection.Close 吗?还是其中一个隐含了另一个?
这些对象最终会被垃圾收集器处理掉吗(可能不及时)?还是这些对象隐式地需要处理,可能是因为使用了非托管代码?
public void GetData(string studentID)
    {
        SqlCommand cmd = new SqlCommand("sp_stored_proc", 
                new SqlConnection(Settings.Default.connectionString)) 
                { CommandType = CommandType.StoredProcedure };
        try
        {
            cmd.Connection.Open();
            cmd.Parameters.AddWithValue("@student_id", studentID);
            SqlDataReader dr = cmd.ExecuteReader();

         //do something with the data

            if (dr != null)
                dr.Dispose();
        }
        catch
        {
            //error handling
        }
        finally
        {
            if (cmd != null)
            {
                cmd.Dispose();
                cmd.Connection.Close();
            }

        }

    }
6个回答

18

您应该处理数据读取器和命令。如果您处理命令,则不需要单独关闭连接。最好使用 using 块同时处理两个对象:

using (SqlCommand cmd = new...)
{
    // do stuff
    using (SqlDataReader dr = cmd.ExecuteReader())
    {
        // do stuff
    }
}

如果需要异常处理,请单独在using块内或外执行,但不需要为Dispose调用使用finally


1
如果我可以给这个点赞多次,我会的。快使用 using 块! - Andy_Vulhop
2
释放 SqlCommand 对象将不会释放 SqlConnection。这很容易测试。https://dev59.com/G3VD5IYBdhLWcg3wL4cA#60934 - arcain
即使我处理了命令,我是否必须处理读取器? 即使我处理了连接,我是否必须处理命令? 您能否向我们提供任何相关来源? - Lii

3

由于SqlDataReader在try catch语句块内部,我需要释放它吗?

-- 是的,因为在try catch语句块中,并不会调用dispose方法。

我需要同时运行cmd.Dispose和cmd.Connection.Close吗?还是其中一个包含另一个?

-- 是的,你需要同时运行两个方法。调用Cmd.dispose并不能关闭连接。

dispose 方法旨在由程序员用于清理资源,这些资源要么不由垃圾回收器直接管理,要么需要在程序使用完它们后清除以释放空间。从技术上讲,可以设置程序,使GC处理其处理,但这是我不会做出的假设,特别是因为编写类的程序员为您公开了dispose方法。将命令放入using语句中可能是最简单的方法,因为当代码离开声明空间时,您知道它将被处置。

using (var connection  = new Connection ())
{
   using (var cmd = new Command())
   {



   }
}

不,cmd.dispose会关闭连接 - 我认为这是不正确的;据我所知,在命令上调用Dispose与其连接无关。 - Fredrik Mörk
@Kevin:你链接的帖子指出,在连接对象上调用Dispose将会在同一对象上调用close。页面上没有出现“command”这个词。 - Fredrik Mörk
我不敢相信我犯了那个错误。在我思考了一秒钟之后,完全可以理解命令对象不会关闭连接对象。哎呀! - kemiller2002

3

如果你使用类似于这个的东西:

public void GetData(string studentID)
{
    using (SqlConnection connection = new SqlConnection(Settings.Default.connectionString))
    {
        connection.Open();

        using (SqlCommand command = connection.CreateCommand())
        {
            command.CommandType = CommandType.StoredProcedure;
            command.CommandText = "sp_stored_proc";
            command.Parameters.AddWithValue("@student_id", studentID);

            using (SqlDataReader dataReader = command.ExecuteReader())
            {
                // do something with the data
            }
        }
    }
}

使用using块可以确保所有的可释放对象都被正确释放。当using块退出时,它会调用SqlConnection、SqlCommand和SqlDataReader对象的Dispose()方法,从而正确关闭它们。

此外,这种方法使得所有变量的作用域都限定在它们被使用的地方。

这种方法的缺点是,如果你需要使用try/catch进行错误处理,你必须将其包装在整个方法体周围,或者使用多个try/catch来处理连接错误和读取错误等不同类型的错误...


2

个人认为,如果某个对象有dispose方法,则无论如何都值得使用,因为它们可以防止潜在的内存泄漏。


1
长话短说,如果实现了IDisposable,应该调用Dispose
即使您使用Reflector找出一个对象中的Dispose调用另一个对象的Dispose,我仍然建议同时调用Dispose,因为这是内部实现细节,可能会在未来的版本中更改,所以您不应该依赖它始终如一的真实性。
因此,Dispose任何IDisposable的内容。

-4

首先,您应该通过 Connection.Open() 打开连接; 然后使用 SqlDataReader 等方法进行读取; 最后,首先关闭 SqlDataReader,然后再关闭连接。

您可以使用关键字“using”来处理它,但这不是一个好主意。

实际上,“using”关键字是自动处理对象的释放。 换句话说,对象应该实现 dispose 方法。


请详细阐述为什么在这种情况下使用using结构是一个不好的主意。 - LOAS

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