我注意到在使用语句中从IDataReader
读取一些我无法理解的好奇心。虽然我确定答案很简单。
为什么在using (SqlDataReader rd) { ... }
内部,如果我直接执行yield return
,读者在阅读期间保持打开状态。但是,如果我执行直接return
调用SqlDataReader扩展方法(如下所述),读者在枚举可以实现之前关闭?
public static IEnumerable<T> Enumerate<T>(this SqlDataReader rd)
{
while (rd.Read())
yield return rd.ConvertTo<T>(); //extension method wrapping FastMember
rd.NextResult();
}
为了完全清楚我所问的内容,我不确定为什么以下内容根本不同:
一个充实的例子,根据@ TimSchmelter的要求:
/*
* contrived methods
*/
public IEnumerable<T> ReadSomeProc<T>() {
using (var db = new SqlConnection("connection string"))
{
var cmd = new SqlCommand("dbo.someProc", db);
using(var rd = cmd.ExecuteReader())
{
while(rd.Read())
yield return rd.ConvertTo<T>(); //extension method wrapping FastMember
}
}
}
//vs
public IEnumerable<T> ReadSomeProcExt<T>() {
using (var db = new SqlConnection("connection string"))
{
var cmd = new SqlCommand("dbo.someProc", db);
using(var rd = cmd.ExecuteReader())
{
return rd.Enumerate<T>(); //outlined above
}
}
}
/*
* usage
*/
var lst = ReadSomeProc<SomeObect>();
foreach(var l in lst){
//this works
}
//vs
var lst2 = ReadSomeProcExt<SomeObect>();
foreach(var l in list){
//throws exception, invalid attempt to read when reader is closed
}
答案 0 :(得分:10)
摘要:推迟的方法的两个版本,但由于
ReadSomeProcExt
不会延迟执行,因此在执行传回之前处理读取器到调用者(即Enumerate<T>
可以运行之前)。另一方面,ReadSomeProc
在读者被传递回调用者之前不会创建读者,因此在读取所有值之前,它不会处置容器。
当您的方法使用yield return
时,编译器实际上会更改已编译的代码以返回IEnumerable<>
,而方法中的代码将无法运行,直到其他代码开始迭代返回的{ {1}} 强>
这意味着下面的代码在放置阅读器并返回值之前甚至不会运行IEnumerable<>
方法的第一行。当其他人开始迭代您返回的Enumerate
时,读者已经被处理掉了。
IEnumerable<>
但是此代码会执行整个using(SqlDataReader rd = cmd.ExecuteReader()){
return rd.Enumerate<T>();
}
方法,以便在返回之前生成Enumerate()
个结果:
List<>
另一方面,无论是谁用这段代码调用方法,都不会实际执行该方法,直到评估结果为止:
using(SqlDataReader rd = cmd.ExecuteReader()){
return rd.Enumerate<T>().ToList();
}
但是当他们执行返回的using(SqlDataReader rd = cmd.ExecuteReader()){
while(rd.Read())
yield return rd.ConvertTo<T>(); //extension method wrapping FastMember
}
时,IEnumerable<>
块会打开,并且using
不会Dispose()
直到IEnumerable<>
完成迭代,在这一点上,您已经从数据阅读器中读取了所需的一切。
答案 1 :(得分:0)
这是因为&#34;收益率回报&#34;将返回一个元素并继续迭代,而#34; normal&#34; return将完成调用。