yield return vs. return IEnumerable

后端 未结 2 1531
礼貌的吻别
礼貌的吻别 2021-02-19 07:02

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.

2条回答
  •  既然无缘
    2021-02-19 07:37

    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 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.

    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();
    }
    

    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().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(); //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.

提交回复
热议问题