使用中的产量回报

如果我没记错的话,当我在using SqlConnection块时使用yield时,我得到了运行时exception。

 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(); } 

当我用List替换yield时,我解决了这些问题,我在每次迭代中都添加了项目。

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

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

有没有解释为什么例外事件发生在前一种情况而不是后者? 这个建筑是否可取?

编辑为了得到我过去做的错误(早期处理)你应该调用下面的第一个方法:

 IEnumerable Read(string fileName) { using (var streamReader = new StreamReader(fileName)) { return Read(streamReader); } // Dispose will be executed before ReadLine() because of deffered execution } IEnumerable Read(StreamReader streamReader) { string line; while ((line = streamReader.ReadLine()) != null) { yield return line; } } 

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

有关usingyield问题的详细解释,请参阅此文章 。 因为您在枚举器中返回,所以在访问任何内容之前,using块已经破坏了上下文。 答案有很好的解决方案,基本上,要么将包装器方法作为枚举器,要么改为构建列表。

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

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

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

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

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