使用中的产量回报
如果我没记错的话,当我在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()
有关using
和yield
问题的详细解释,请参阅此文章 。 因为您在枚举器中返回,所以在访问任何内容之前,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