代码分析工具

本文关键字:工具 代码 | 更新日期: 2023-09-27 18:11:12

我正在使用Visual Studio的代码分析工具,它给我的警告之一是"不要多次处理对象:对象'conn'可以在方法' cyclemessages . distincnscan_reprint()'中多次处理"。以避免生成系统。你不应该在一个对象上多次调用Dispose。线:61

    private void discernScan_Reprint()
    {
        try
        {
            DataTable dt = new DataTable();
            SqlConnection conn = new SqlConnection("my constring");
            using (conn)
            {
                SqlCommand cmd = new SqlCommand("usp_myproc", conn);
                cmd.CommandType = CommandType.StoredProcedure;
                using(cmd)
                {
                    cmd.Parameters.Add(new SqlParameter("@param", param));
                    conn.Open();
                    SqlDataReader dr = cmd.ExecuteReader();
                    dt.Load(dr);
                    conn.Close();  // this is line 61
                }
            }
            switch(dt.Rows.Count)
            {
                default:
                    throw new Exception("message");
                case 1:
                    //do Stuff
                case 0:
                    //do stuff
                    break;
            }
        }
        catch (Exception ex) {throw;}
    }

我不处置conn(显式地通过conn. dispose();),我只是关闭它,并允许使用封装来处置conn对象-我知道我可以允许它通过处置被关闭,但为什么它说我要处置它两次?如果有的话,它应该警告我说"你不需要终止将被处置的对象上的连接"或类似的东西。我错过什么了吗?

编辑:

Close方法回滚所有挂起的事务。然后将连接释放到连接池,如果连接池被禁用,则关闭连接。

如果SqlConnection超出作用域,它将不会被关闭。因此,必须通过调用close或Dispose显式地关闭连接。Close和Dispose在功能上是等价的。如果连接池值"pooling"设置为true或yes,底层连接将返回到连接池。另一方面,如果Pooling设置为false或no,则关闭与服务器的底层连接。

我知道Close()在功能上与dispose相同,但从我的理解来看,这不是字面上的。当我关闭对象时,它没有被处理掉。它要么被关闭,要么被返回到连接池,在内部调用close()方法(还是根据我的理解)——所以虽然冗余,但我仍然感到困惑,为什么它显式地说它已经被处理了,当它不是。

代码分析工具

Dispose模式建议实现者为Dispose提供在对象上下文中有意义的同义词。SqlConnection上的一个同义词是Close():

Close和Dispose在功能上是等价的。

由于您显式地调用Close(),并且当连接的using语句结束时正在调用对象的Dispose()方法,因此您实际上调用了两次Dispose()

最好的方法是让using块为您处理它,因为它保证即使在using块内部发生异常时也会调用Dispose()。它还将变量设置为null,以便可以尽快对其进行GC处理。


编辑回复@alykin的问题

文档说Close()Dispose()方法在功能上是等同的,但是@alykin已经确定了一个场景,它们实际上并没有做同样的事情。如果我没看错的话,它的工作原理是这样的:

以下作品:

SqlConnection conn = GetConnSomehow();
SqlCommand cmd = conn.CreateCommand(); 
// ...
conn.Open();
cmd.ExecuteSomething();
cmd.Close();
// ... time passes ...
conn.Open();

SqlConnection conn = GetConnSomehow();
SqlCommand cmd = conn.CreateCommand(); 
using ( conn ) {
    cmd.ExecuteSomething();
}
// ... time passes ...
// This won't compile, according to alykins.
conn.Open();

这表明SqlConnection对象可以被重用,至少当它们只有Close() 'd时。

using块的第二个例子没有编译的原因可能是编译器知道connusing块结束时被设置为null,所以它知道你不能在null对象引用上调用方法。

我仍然不确定这是否表明Dispose()实际上与Close()有任何不同,因为不一致是由于using块的空语义引起的。这将是值得测试是否一个SqlConnection可以重新打开后,它是Dispose() 'd,但不空。即使它是,我也不会依赖于这种行为,因为它违背了微软自己在Dispose Pattern文档中设置的指导方针。

另外,我不会使用第一个不使用using块的块——如果发生异常,连接可能会泄漏,或者至少在不确定的时间内保持打开状态,直到GC看到对象已经泄漏并调用其终结器。

我不会依赖Close()Dispose()之间的任何行为差异-我建议不要尝试重新打开先前关闭的SqlConnection对象。让池处理程序处理实际保持连接活动,即使您关闭或处置了交给您的SqlConnection对象。


使用语句的注意事项。

考虑以下代码块:

IDisposable thing = GetThing();
using ( thing ) {
   thing.DoWork();
}

代码块与这个代码块完全相同:

IDisposable thing = GetThing();
try {
   thing.DoWork();
}
finally {
   thing.Dispose();
   thing = null;
}

下面的代码块,按照微软、他们的文档和他们的分析工具的考虑,被视为两个处理:

SqlConnection conn = GetConn();
using ( conn ) {
    DoWork(conn);
    conn.Close(); // code analysis tools count this as one Dispose().
} // implicit conn.Dispose() from the using block, so that's two.

忽略Close和Dispose并不完全做相同的事情这一事实。他们不希望您依赖于此,也不希望您依赖于此,以防行为确实得到修复。

它通知您显式关闭提前处置了资源。using语句将自动为您清除它。

根据MSDN:

Close和Dispose在功能上是等价的。

因此,调用.Close()释放对象。另外,由于对象在using块中,编译器也调用.Dispose()。两者中只需要一个。(在这种情况下建议使用后者)

本质上,你只需要删除对Close()的调用,因为using块将在处置对象时为你处理它:

using (conn)
{
    SqlCommand cmd = new SqlCommand("usp_myproc", conn);
    cmd.CommandType = CommandType.StoredProcedure;
    using(cmd)
    {
        cmd.Parameters.Add(new SqlParameter("@param", param));
        conn.Open();
        System.Data.SqlClient.SqlDataReader dr = cmd.ExecuteReader();
        dt.Load(dr);
    }
}

close();dispose();基本上做同样的事情,这就是为什么你收到这个警告。点击此链接获取更多信息