c# using 语句 with double IDisposable

c# using statement with double IDisposable

我有以下使用 using 的语句:

using (var reader = data.CreateCommand(sql).ExecuteDataReader())

在这种情况下 data 是内部保存 SqlConnection 的某个对象。 CreateCommand(sql) 函数 returns SqlCommandExecuteDataReader returns SqlDataReader。由于 SqlCommandSqlDataReader 都是 IDisposable,因此使用 using 语句会同时处理它们吗?

目前我是这样做的:

using (var cmd = data.CreateCommand(sql))
using (var reader = cmd.ExecuteDataReader())

但我想知道是否可以按照上述方式将它们组合起来?

同意 Matthew Watson 的评论。您需要同时拥有 usings 个状态。 这是带有附加推理的相关问题。 SqlConnection SqlCommand SqlDataReader IDisposable

"if it's possible to combine them" - 两个 using 完全没问题,因为两个都需要处理掉。

您可以通过提取到方法中将它们组合起来:

void RunWithReader(string sql, Action<SQLDataReader> action)
{
    using (var cmd = data.CreateCommand(sql))
    using (var reader = cmd.ExecuteDataReader())
        action(reader);
}

那你就可以使用lambda了

RunWithReader("whatever command", reader =>
{
    ... // while(reader.Read() { ... } - this could also be extracted
});

您在内部 IDisposable (IDbCommand) 中呈现代码的方式未被处理。

你有两个选择:

你可以像这样把它们全部放在一个 using 中:

using (IDisposable cmd = data.CreateCommand(), reader = ((IDbCommand)cmd).ExecuteReader())
{
    // code here
}

但是这样比较麻烦。另一个选项是嵌套 using 语句:

using (var cmd = data.CreateCommand())
{
    using (var reader = cmd.ExecuteReader())
    {
        // code here
    }
}

除此之外,您可能会变得有点复杂并编写一个扩展方法来帮助(某种程度上)为您清理它。

public static class Ex
{
    public static void Using<T, R>(this T obj, Func<T, R> create, Action<T, R> use) where R : IDisposable
    {
        using (var d = create(obj))
        {
            use(obj, d);
        }
    }
}

那么你可以这样做:

data.Using(d => d.CreateCommand(), (d, c) => c.Using(c2 => c2.ExecuteReader(), (c3, r) =>
{
    // code here
}));

但也许这并没有太大的改善。

一般

You should Dispose every class that implements IDisposable.

但是,你的问题是专门处理一个场景所有资源都是相关的,如果我的理解是正确的,垃圾收集器会自动为你处理这些资源.


Finalize() 方法("Destructors" 或 "Finalizers")

The Finalize() method makes sure that resources are disposed when the instance is no longer referenced by the application.

配合Dispose()方法

Dispose() 方法将 "release all resources used by the component"。这意味着此代码块(取自 MSDN's ExecuteReader Method Example)将正确处理它使用的所有组件...

using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();

    SqlCommand command = new SqlCommand(queryString, connection);
    SqlDataReader reader = command.ExecuteReader();
    while (reader.Read())
    {
        // ...
    }
}

In this case, when disposing SqlConnection, it will properly release all resources used by the component. Which means:

  • SqlCommand
  • SqlDataReader

    ** Note: This has always been my understanding...


将上述代码重构为以下代码:

using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();

    using (SqlCommand command = new SqlCommand(queryString, connection))
    {
        using (SqlDataReader reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                // ...
            }
        }
    }
}

The main advantage of manually disposing of each resource, would be for a performance gain since we are not processing up to the Finalize method (which is relatively a more expensive than doing Dispose().

但是,这并不意味着您想要 Dispose() 一切,因为在某些情况下 Finalize() 方法是更好的选择。


假设到目前为止我仍然处于良好状态,并且我对垃圾收集器行为的理解是有效的。

我会考虑 SqlCommand(完整代码参考 here),然后也会处理 SqlReader,这意味着您可以逃脱:

using (SqlCommand command = new SqlCommand(queryString, connection))
{
    SqlDataReader reader = command.ExecuteReader();
    while (reader.Read())
    {
        // ...
    }
}

在您的情况下将转换为:

using (var cmd = data.CreateCommand(sql))
{
    var reader = cmd.ExecuteDataReader();
    // ...
}