收益率与收益率IEnumerable <t>

时间:2017-08-15 15:14:20

标签: c# ado.net ienumerable 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();
}

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

  

一个充实的例子,根据@ 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
}

2 个答案:

答案 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将完成调用。