问题描述
我注意到我对无法理解的using语句中的 IDataReader 进行读取感到有些好奇。虽然我确信答案很简单。
为什么在内使用(SqlDataReader rd){...} 如果我直接执行收益率,则阅读器在读取期间保持打开状态。但是,如果我执行直接的 return 调用SqlDataReader扩展方法(如下所示),该方法在实现可枚举之前关闭了读者?
公共静态IEnumerable< T>枚举< T>(此SqlDataReader rd)
{
而(rd.Read())
的收益率返回rd.ConvertTo< T>(); //扩展方法包装FastMember
rd.NextResult();
}
要绝对清楚我要问的是什么,我不确定为什么以下是根本不同的内容:
当您的方法使用 yield return 时,编译器实际上会更改已编译的返回 IEnumerable<> 的代码,并且在其他代码开始对返回的 IEnumerable<进行迭代之前,方法中的代码将不会运行。 > 。
这意味着下面的代码甚至没有运行 Enumerate 方法,然后将其分配给读取器并返回一个值。当其他人开始遍历您返回的 IEnumerable<> 时,阅读器已被处置。
using(SqlDataReader rd = cmd.ExecuteReader()){
返回rd.Enumerate< T>();
}
但是此代码将执行整个 Enumerate() 方法,以便在返回之前生成结果 List<> :
using(SqlDataReader rd = cmd.ExecuteReader()){
返回rd.Enumerate< T>()。ToList();
}
另一方面,谁叫呼叫方法使用此代码,直到对结果求值才真正执行该方法:
using(SqlDataReader rd = cmd.ExecuteReader() ){
while(rd.Read())
收益回报率rd.ConvertTo< T>(); //扩展方法包装FastMember
}
但是当它们执行返回的 IEnumerable<> ,打开 using 块,而不显示 Dispose(),直到 IEnumerable<> 完成其迭代为止,此时您已经从数据读取器中读取了所需的所有内容。
I've noticed something curious about reading from an IDataReader within a using statement that I can't comprehend. Though I'm sure the answer is simple.
Why is it that whilst inside the using (SqlDataReader rd) { ... } if I directly perform a yield return the reader stays open for the duration of the read. But if I perform a direct return calling a SqlDataReader extension method (outlined below) that the reader closes before the enumerable can be actualized?
public static IEnumerable<T> Enumerate<T>(this SqlDataReader rd) { while (rd.Read()) yield return rd.ConvertTo<T>(); //extension method wrapping FastMember rd.NextResult(); }
To be absolutely clear of what I'm asking, I'm unsure why the following are fundamentally different:
/* * 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 }
When your method uses yield return, the compiler actually changes the compiled code to return an IEnumerable<>, and the code in your method will not run until other code starts iterating over the returned IEnumerable<>.
That means that the code below doesn't even run the first line of your Enumerate method before it disposes the reader and returns a value. By the time someone else starts iterating over your returned IEnumerable<>, the reader has already been disposed.
using(SqlDataReader rd = cmd.ExecuteReader()){ return rd.Enumerate<T>(); }
But this code would execute the entire Enumerate() method in order to produce a List<> of results prior to returning:
using(SqlDataReader rd = cmd.ExecuteReader()){ return rd.Enumerate<T>().ToList(); }
On the other hand, whoever's calling the method with this code doesn't actually execute the method until the result is evaluated:
using(SqlDataReader rd = cmd.ExecuteReader()){ while(rd.Read()) yield return rd.ConvertTo<T>(); //extension method wrapping FastMember }
But the moment they execute the returned IEnumerable<>, the using block opens up, and it doesn't Dispose() until the IEnumerable<> finishes its iterations, at which point you will have already read everything you need from the data reader.
这篇关于收益回报与收益IEnumerable T的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!