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()
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.
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.
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.
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.
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];
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With