使用内收益回报

发布于 2024-11-02 09:42:53 字数 1391 浏览 1 评论 0原文

如果我没记错的话,当我在 using SqlConnection 块中使用产量时,我遇到了运行时异常。

using (var connection = new SqlConnection(connectionString))
{
    var command = new SqlCommand(queryString, connection);
    connection.Open();

    SqlDataReader reader = command.ExecuteReader();

    // Call Read before accessing data.
    while (reader.Read())
    {
        yield reader[0];
    }

    // Call Close when done reading.
    reader.Close();
}

当我用一个列表替换 yield 时,这些问题得到了解决,我在每次迭代中添加了项目。

当在 using StreamReader 块内时,同样的问题还没有发生在我身上

using (var streamReader = new StreamReader(fileName))
{
    string line;
    while ((line = streamReader.ReadLine()) != null)
    {
        yield return line;
    }
}

是否有任何解释为什么异常发生在前一种情况而不是后者?这样的施工是否可取?

编辑要获得我过去所做的错误(早期处置),您应该调用下面的第一个方法:

IEnumerable<string> Read(string fileName)
{
    using (var streamReader = new StreamReader(fileName))
    {
        return Read(streamReader);
    } // Dispose will be executed before ReadLine() because of deffered execution
}

IEnumerable<string> Read(StreamReader streamReader)
{
    string line;
    while ((line = streamReader.ReadLine()) != null)
    {
        yield return line;
    }
}

使用其他延迟执行的方法可以实现相同的错误,例如System. Linq.Enumerable.Select()

If I recall correctly that when I used yield inside using SqlConnection blocks I got runtime exceptions.

using (var connection = new SqlConnection(connectionString))
{
    var command = new SqlCommand(queryString, connection);
    connection.Open();

    SqlDataReader reader = command.ExecuteReader();

    // Call Read before accessing data.
    while (reader.Read())
    {
        yield reader[0];
    }

    // Call Close when done reading.
    reader.Close();
}

Those problems were solved when I replaced yield by a List where I added items each iteration.

The same problem didn't happen yet to me when inside using StreamReader blocks

using (var streamReader = new StreamReader(fileName))
{
    string line;
    while ((line = streamReader.ReadLine()) != null)
    {
        yield return line;
    }
}

Is there any explanation why Exceptions happened in the former case and not in the latter? Is this construction advisable?

EDIT To get the error (early disposal) that I did in the past you should call the first method below:

IEnumerable<string> Read(string fileName)
{
    using (var streamReader = new StreamReader(fileName))
    {
        return Read(streamReader);
    } // Dispose will be executed before ReadLine() because of deffered execution
}

IEnumerable<string> Read(StreamReader streamReader)
{
    string line;
    while ((line = streamReader.ReadLine()) != null)
    {
        yield return line;
    }
}

The same error can be achieved with other ways of deferring execution, such as System.Linq.Enumerable.Select()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

黑白记忆 2024-11-09 09:42:53

请参阅这篇文章以获得很好的解释usingyield 的问题。因为您在枚举器中返回,所以 using 块在访问任何内容之前就已经销毁了上下文。答案有很好的解决方案,基本上,要么使包装器方法成为枚举器,要么构建一个列表。

此外,在读取器周围使用 using(而不是连接)通常更实用,并使用 CommandBehavior.CloseConnection 来确保读取器完成时释放资源。尽管这在您的情况下并不重要,但如果您从方法返回数据读取器,这将确保在处理读取器时正确关闭连接。

   using(SqlDataReader reader = 
             command.ExecuteReader(CommandBehavior.CloseConnection)) {
        while (reader.Read())
        {
            yield reader[0];
        }
   }

See this post for a good explanation of the issues with using and yield. Because you return in enumerator, the using block will already have destroyed the context before anything is accessed. The answers have good solutions, basically, either make the wrapper method an enumerator, or build a list instead.

Also it's usually more practical to have using around the reader, not the connection, and use CommandBehavior.CloseConnection to ensure resources are released when the reader's done. Though it doesn't really matter in your situation, if you ever return a data reader from a method, this will ensure the connection is closed properly when the reader is disposed.

   using(SqlDataReader reader = 
             command.ExecuteReader(CommandBehavior.CloseConnection)) {
        while (reader.Read())
        {
            yield reader[0];
        }
   }
汹涌人海 2024-11-09 09:42:53

在这两种情况下,编译器都应该正确处理 using 块内的 yield。没有明显的理由说明为什么它应该抛出异常。

需要注意的一件事是,只有在完成迭代和/或手动处置枚举器对象后,连接才会被处置。如果您在公共方法中公开此代码,那么愚蠢或恶意代码可能会使您的连接长时间保持打开状态:

 var enumerable = YourMethodThatYieldsFromTheDataReader();
 var enumerator = enumerable.GetEnumerator();
 enumerator.MoveNext();
 Thread.Sleep(forever);    // your connection will never be disposed

The compiler should handle the yield inside the using block correctly in both cases. There's no obvious reason why it should throw an exception.

One thing to be aware of is that the connection will only be disposed once you've completed iterating and/or manually disposed the enumerator object. If you expose this code in a public method then it's possible that stupid or malicious code could keep your connection open for a long time:

 var enumerable = YourMethodThatYieldsFromTheDataReader();
 var enumerator = enumerable.GetEnumerator();
 enumerator.MoveNext();
 Thread.Sleep(forever);    // your connection will never be disposed
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文