简体   繁体   English

代码分析工具

[英]Code Analysis tool

I am using Visual Studio's code analysis tool, and one of the warnings it gives me is "Do not dispose objects multiple times: Object 'conn' can be disposed more than once in method 'CycleMessages.discernScan_Reprint()'. To avoid generating a System.OjectDisposedEx you should not call Dispose more than one time on an object. Line:61 我正在使用Visual Studio的代码分析工具,它给我的警告之一是“不要多次处置对象:在方法'CycleMessages.discernScan_Reprint()'中可以多次处置对象'conn'。 System.OjectDisposedEx,您不应在对象上多次调用Dispose。Line: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;}
    }

I don't dispose the conn (explicitly via conn.Dispose();), I just close it and allow the using encapsulation to dipose of the conn object- I know I can allow it to be closed via the disposal, but why is it saying I'm disposing it twice? 我不处置conn(通过conn.Dispose()明确地);我只是将其关闭,并允许使用封装来丢弃conn对象-我知道我可以通过处置将其关闭,但是为什么呢?它说我要处置两次? If anything it should warn me saying "You don't need to terminate connections on objects that will be disposed" or something like that. 如果有的话,它应该警告我说“您不需要终止将要处置的对象的连接”或类似的东西。 Am I missing something? 我想念什么吗?

Edits: From ref link on close... 编辑:从引用链接上关闭...

The Close method rolls back any pending transactions. Close方法将回滚任何未决的事务。 It then releases the connection to the connection pool, or closes the connection if connection pooling is disabled. 然后,它将连接释放到连接池,或者如果禁用了连接池,则关闭连接。

and

If the SqlConnection goes out of scope, it won't be closed. 如果SqlConnection超出范围,则不会关闭它。 Therefore, you must explicitly close the connection by calling Close or Dispose. 因此,您必须通过调用Close或Dispose显式关闭连接。 Close and Dispose are functionally equivalent. Close和Dispose在功能上是等效的。 If the connection pooling value Pooling is set to true or yes, the underlying connection is returned back to the connection pool. 如果连接池的值Pooling设置为true或yes,则基础连接将返回到连接池。 On the other hand, if Pooling is set to false or no, the underlying connection to the server is closed. 另一方面,如果Pooling设置为false或no,则关闭与服务器的基础连接。

I know that functionally Close() is the same as dispose, but that is not literal from my understanding. 我知道Close()在功能上与dispose相同,但是根据我的理解,这不是字面意思。 When I close the object it is not disposed. 当我关闭对象时,它不会被丢弃。 It is either closed or returned to the connection pool, disposing (again from my understanding) internally calls the close() methods- so while redundant, I still am baffled as to why it is explicitly saying it is already disposed, when it's not. 它要么关闭,要么返回到连接池,在内部处理(再次根据我的理解)在内部调用close()方法-因此,尽管冗余,但对于为什么要明确指出它已经被处理(如果尚未被处理),我仍然感到困惑。

The Dispose pattern suggests that implementors provide synonyms for Dispose that make sense in the context of the object. Dispose模式建议实现者为Dispose提供在对象上下文中有意义的同义词。 One such synonym on SqlConnection is Close() : SqlConnection上的此类同义词之一是Close()

Close and Dispose are functionally equivalent. Close和Dispose在功能上是等效的。

Since you're explicitly calling Close() , and the object's Dispose() method is being called when the connection's using statement ends, you're effectively calling Dispose() twice. 由于您显式地调用Close() ,并且在连接的using语句结束时将调用对象的Dispose()方法,因此您实际上已两次调用了Dispose()

The best approach is to just let the using block handle it for you, since it guarantees that Dispose() is called even when an exception occurs from inside the using block. 最好的方法是让using块为您处理它,因为即使在using块内部发生异常,它也保证可以调用Dispose() It also sets the variable to null so that it can be GC'd as soon as possible. 它还将变量设置为null,以便可以尽快对其进行GC处理。


Edits to respond to @alykin's questions 编辑以回复@alykin的问题

The documentation says that the Close() and Dispose() methods are functionally equivalent, but @alykin has identified a scenario where they don't actually do the same thing. 该文档说Close()Dispose()方法在功能上是等效的,但是@alykin已经确定了一种情况,它们实际上并没有做同样的事情。 If I'm reading her comment correctly, it works something like this: 如果我正确阅读了她的评论,它的工作原理如下:

The following works: 以下作品:

SqlConnection conn = GetConnSomehow();
SqlCommand cmd = conn.CreateCommand(); 

// ...

conn.Open();

cmd.ExecuteSomething();

cmd.Close();

// ... time passes ...

conn.Open();

The following doesn't: 以下不是:

SqlConnection conn = GetConnSomehow();
SqlCommand cmd = conn.CreateCommand(); 

using ( conn ) {
    cmd.ExecuteSomething();
}

// ... time passes ...

// This won't compile, according to alykins.
conn.Open();

This shows that SqlConnection objects can be reused, at least when they've been only Close() 'd. 这表明SqlConnection对象至少在仅使用Close()时才可以重用。

Likely the reason why the second example with the using block doesn't compile is that the compiler knows that conn has been set to null when the using block ends, so it knows that you can't call methods on a null object reference. 带有using块的第二个示例无法编译的原因可能是,编译器知道在using块结束时conn已设置为null,因此它知道您不能在null对象引用上调用方法。

I'm still not sure that this shows that a Dispose() is actually any different than a Close() though, since the incongruity arises due to the nulling semantic of the using block. 我仍然不确定这是否表明Dispose()实际上与Close()有所不同,因为不一致之处是由于using块的空语义而引起的。 It would be worthwhile testing whether or not an SqlConnection can be re-opened after it is Dispose() 'd but not nulled. 值得测试的是在Dispose()但不为空之后是否可以重新打开SqlConnection。 Even if it were, I wouldn't rely on that behavior since it is counter to Microsoft's own guidelines set in the Dispose Pattern documentation . 即使是这样,我也不会依赖该行为,因为它与Microsoft在Dispose Pattern文档中设置的准则背道而驰。

Additionally, I would not use the first block that doesn't use a using block - if an exception occurs, the connection may be leaked, or at least, held open for a non-deterministic amount of time until the GC sees that the object has been leaked and invokes its finalizer. 另外,我不会使用不使用using块的第一个块-如果发生异常,则连接可能会泄漏,或者至少在不确定的时间内保持打开状态,直到GC看到该对象为止已泄漏并调用其终结器。

I would not rely on any difference in behavior between Close() and Dispose() - I would recommend against attempting to re-open a previously-closed SqlConnection object. 我不会依赖Close()Dispose()之间行为上的任何差异-我建议不要尝试重新打开以前关闭的SqlConnection对象。 Let the pooler handle actually keeping the connection alive even if you close or dispose the SqlConnection object you were handed. 让池管理器实际上使连接保持活动状态,即使您关闭或处置您已处理的SqlConnection对象也是如此。


A note on using statements. 有关使用语句的说明。

Consider the following block of code: 考虑以下代码块:

IDisposable thing = GetThing();

using ( thing ) {
   thing.DoWork();
}

That block of code is exactly identical to this block: 该代码块与此代码块完全相同

IDisposable thing = GetThing();

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

And the following block, as considered by Microsoft, their documentation, and their analysis tools, counts as two disposes: 微软认为,以下内容,其文档和分析工具被视为两个处置:

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.

Ignore the fact that Close and Dispose don't exactly do the same thing. 忽略Close和Dispose并不会完全执行相同操作的事实。 They don't want you to rely on that, nor should you in case the behavior does actually get fixed. 他们不希望您依靠它,也不希望您的行为确实得到解决。

It is informing you that the explicit close is disposing resources early. 它通知您显式关闭正在尽早配置资源。 The using statement will automatically dispose it for you. using语句将自动为您处理它。

close(); and dispose(); dispose(); essentially do the same thing, that is why you are receiving this warning. 本质上做同样的事情,这就是为什么您收到此警告。 Check this link for more information. 检查此链接以获取更多信息。

According to MSDN : 根据MSDN

Close and Dispose are functionally equivalent. Close和Dispose在功能上是等效的。

Thus, calling .Close() disposes of the object. 因此,调用.Close()处理该对象。 Additionally, since the object is in a using block, the compiler also calls .Dispose() . 另外,由于对象位于using块中,因此编译器还会调用.Dispose() Only one of the two is needed. 只需两者之一。 (And the latter is recommended in this case.) (在这种情况下,建议使用后者。)

Essentially, you just need to remove the call to Close() since the using block will handle that for you when it disposes of the object: 本质上,您只需要删除对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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM