如果在执行过程中抛出异常,则可能不会调用 Dispose¶
ID: cs/dispose-not-called-on-throw
Kind: problem
Security severity:
Severity: warning
Precision: medium
Tags:
- efficiency
- maintainability
- external/cwe/cwe-404
- external/cwe/cwe-459
- external/cwe/cwe-460
Query suites:
- csharp-security-and-quality.qls
如果在分配 IDisposable
对象与对该对象进行 Dispose()
调用之间抛出异常,并且 Dispose()
调用不在 catch
或 finally
块内,则 Dispose()
调用可能不会执行。
建议¶
如果可能,请将对象的分配包装在 using
块中,以便在 using
块完成后自动释放对象。
如果无法做到这一点,请确保对对象调用了 Dispose()
。通常建议在 finally
块中调用 Dispose()
,以确保即使抛出异常也能释放对象。
示例¶
在此示例中,创建了一个 SqlConnection
,然后使用 SqlCommand
运行 SQL 查询。创建并释放了对象,但如果抛出异常(例如,通过调用 ExecuteReader
),该方法将立即终止,并且永远不会对 cmd
和 conn
调用 Dispose()
。对于 SqlConnection
,这可能会导致与连接关联的非托管资源被保留,并可能在尝试创建其他未来连接时导致资源耗尽。
using System;
using System.Data.SqlClient;
class Bad
{
public SqlDataReader GetAllCustomers()
{
var conn = new SqlConnection("connection string");
conn.Open();
var cmd = new SqlCommand("SELECT * FROM Customers", conn);
var ret = cmd.ExecuteReader();
cmd.Dispose();
conn.Dispose();
return ret;
}
}
在修改后的示例中,使用了一对 using
语句来确保在语句完成后释放连接和命令。
using System;
using System.Data.SqlClient;
class Good
{
public SqlDataReader GetAllCustomers()
{
using (var conn = new SqlConnection("connection string"))
{
conn.Open();
using (var cmd = new SqlCommand("SELECT * FROM Customers", conn))
{
return cmd.ExecuteReader();
}
}
}
}
参考¶
MSDN:IDisposable 接口。
Microsoft:using 语句(C# 参考)。
常见弱点枚举:CWE-404。
常见弱点枚举:CWE-459。
常见弱点枚举:CWE-460。