收益率与收益率IEnumerable <T>

rob*_*pim 13 c# ienumerable ado.net sqldatareader yield-return

我注意到从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();
}
Run Code Online (Sandbox Code Playgroud)

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

一个充实的例子,根据@ 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
}
Run Code Online (Sandbox Code Playgroud)

Str*_*ior 10

简介:该方法的两个版本都推迟,但由于ReadSomeProcExt不延迟执行,因此在执行传递回调用者之前(即Enumerate<T>可以运行之前)处理读取器.ReadSomeProc另一方面,在将读取器传递回调用者之前不会创建读取器,因此在读取所有值之前,它不会释放容器.

当您的方法使用时yield return,编译器实际上会更改已编译的代码以返回一个IEnumerable<>,并且您的方法中的代码将不会运行,直到其他代码开始迭代返回IEnumerable<>.

这意味着下面的代码甚至不会Enumerate在它处理读取器并返回值之前运行方法的第一行.当其他人开始迭代你的回复时IEnumerable<>,读者已经被处理掉了.

using(SqlDataReader rd = cmd.ExecuteReader()){
    return rd.Enumerate<T>();
}
Run Code Online (Sandbox Code Playgroud)

但是这段代码会执行整个Enumerate()方法,以便List<>在返回之前产生一些结果:

using(SqlDataReader rd = cmd.ExecuteReader()){
    return rd.Enumerate<T>().ToList();
}
Run Code Online (Sandbox Code Playgroud)

另一方面,使用此代码调用方法的任何人在评估结果之前实际上并不执行该方法:

using(SqlDataReader rd = cmd.ExecuteReader()){
    while(rd.Read())
        yield return rd.ConvertTo<T>(); //extension method wrapping FastMember
}
Run Code Online (Sandbox Code Playgroud)

但是当它们执行返回时IEnumerable<>,using块会打开,Dispose()直到IEnumerable<>完成迭代,此时您将已经从数据读取器中读取了所需的所有内容.

  • @PimBrouwers两个解决方案都使用`while(rd.Read())`.工作解决方案是推迟读者处理直到实际完成读取的解决方案.您只需使用调试器逐步执行代码,即可查看每行(包括dispose调用)的运行时间 (2认同)