gpt4 book ai didi

c# - yield 返回与返回 IEnumerable

转载 作者:可可西里 更新时间:2023-11-01 08:10:17 27 4
gpt4 key购买 nike

我注意到在我无法理解的 using 语句中从 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();
}

为了完全清楚我在问什么,我不确定为什么以下内容根本不同:

A fleshed out example, as per @TimSchmelter's request:

/*
* 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
}

最佳答案

Summary: Both versions of the method defer, but because ReadSomeProcExt doesn't defer execution, the reader is disposed before execution is passed back to the caller (i.e. before Enumerate<T> can run). ReadSomeProc, on the other hand, doesn't create the reader until it's been passed back to the caller, so it doesn't dispose the container until all its values have been read.

当您的方法使用 yield return 时,编译器实际上更改了编译后的代码以返回 IEnumerable<> , 并且 在其他代码开始迭代返回的 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 block 打开了,但它没有 Dispose()直到 IEnumerable<>完成它的迭代,此时您已经从数据读取器读取了您需要的所有内容。

关于c# - yield 返回与返回 IEnumerable<T>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45695872/

27 4 0