如果我没记错的话,当我在using SqlConnection
块中使用yield时,我得到了运行时异常。
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<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()
答案 0 :(得分:3)
有关using
和yield
问题的详细说明,请参见this post。因为您在枚举器中返回,所以在访问任何内容之前,using块已经破坏了上下文。答案有很好的解决方案,基本上,要么使包装器方法成为枚举器,要么改为构建列表。
在阅读器周围使用using
而不是连接通常更实际,并且使用CommandBehavior.CloseConnection
来确保在读者完成时释放资源。虽然在您的情况下并不重要,但如果您从方法中返回数据读取器,这将确保在处理器处理时正确关闭连接。
using(SqlDataReader reader =
command.ExecuteReader(CommandBehavior.CloseConnection)) {
while (reader.Read())
{
yield reader[0];
}
}
答案 1 :(得分:2)
在两种情况下,编译器都应正确处理yield
块内的using
。没有明显的理由说它应该抛出异常。
要注意的一件事是,只有在完成迭代和/或手动处理枚举器对象后才会处理连接。如果您在公共方法中公开此代码,那么愚蠢或恶意代码可能会使您的连接长时间保持打开状态:
var enumerable = YourMethodThatYieldsFromTheDataReader();
var enumerator = enumerable.GetEnumerator();
enumerator.MoveNext();
Thread.Sleep(forever); // your connection will never be disposed