Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Yield return inside usings

If I recall correctly that when I used yield inside using SqlConnection blocks I got runtime exceptions.

using (var connection = new SqlConnection(connectionString))
{
    var command = new SqlCommand(queryString, connection);
    connection.Open();

    SqlDataReader reader = command.ExecuteReader();

    // Call Read before accessing data.
    while (reader.Read())
    {
        yield reader[0];
    }

    // Call Close when done reading.
    reader.Close();
}

Those problems were solved when I replaced yield by a List where I added items each iteration.

The same problem didn't happen yet to me when inside using StreamReader blocks

using (var streamReader = new StreamReader(fileName))
{
    string line;
    while ((line = streamReader.ReadLine()) != null)
    {
        yield return line;
    }
}

Is there any explanation why Exceptions happened in the former case and not in the latter? Is this construction advisable?

EDIT To get the error (early disposal) that I did in the past you should call the first method below:

IEnumerable<string> Read(string fileName)
{
    using (var streamReader = new StreamReader(fileName))
    {
        return Read(streamReader);
    } // Dispose will be executed before ReadLine() because of deffered execution
}

IEnumerable<string> Read(StreamReader streamReader)
{
    string line;
    while ((line = streamReader.ReadLine()) != null)
    {
        yield return line;
    }
}

The same error can be achieved with other ways of deferring execution, such as System.Linq.Enumerable.Select()

like image 355
Jader Dias Avatar asked Apr 19 '11 12:04

Jader Dias


People also ask

How does yield return work?

When a yield return statement is reached in the iterator method, expression is returned, and the current location in code is retained. Execution is restarted from that location the next time that the iterator function is called.

What does yield break return?

It specifies that an iterator has come to an end. You can think of yield break as a return statement which does not return a value. For example, if you define a function as an iterator, the body of the function may look like this: for (int i = 0; i < 5; i++) { yield return i; } Console.

What is the use of the yield keyword?

The yield keyword is use to do custom stateful iteration over a collection. The yield keyword tells the compiler that the method in which it appears is an iterator block. yield return <expression>; yield break; The yield return statement returns one element at a time.

What is yield in C# with example?

The yield contextual keyword actually does quite a lot here. The function returns an object that implements the IEnumerable<object> interface. If a calling function starts foreach ing over this object, the function is called again until it "yields". This is syntactic sugar introduced in C# 2.0.


1 Answers

See this post for a good explanation of the issues with using and yield. Because you return in enumerator, the using block will already have destroyed the context before anything is accessed. The answers have good solutions, basically, either make the wrapper method an enumerator, or build a list instead.

Also it's usually more practical to have using around the reader, not the connection, and use CommandBehavior.CloseConnection to ensure resources are released when the reader's done. Though it doesn't really matter in your situation, if you ever return a data reader from a method, this will ensure the connection is closed properly when the reader is disposed.

   using(SqlDataReader reader = 
             command.ExecuteReader(CommandBehavior.CloseConnection)) {
        while (reader.Read())
        {
            yield reader[0];
        }
   }
like image 98
Jamie Treworgy Avatar answered Oct 01 '22 18:10

Jamie Treworgy