关于收益率回报和对foreach的打破

Ral*_*ton 24 .net c#

有没有一种正确的方法可以打破foreach,以便IEnumerable <>知道我已经完成并且应该清理它.

请考虑以下代码:

    private static IEnumerable<Person> getPeople()
    {
        using (SqlConnection sqlConnection = new SqlConnection("..."))
        {
            try
            {
                sqlConnection.Open();
                using (SqlCommand sqlCommand = new SqlCommand("select id, firstName, lastName from people", sqlConnection))
                {

                    using (SqlDataReader reader = sqlCommand.ExecuteReader())
                    {
                        while (reader.Read())
                            yield return new Person(reader.GetGuid(0), reader.GetString(1), reader.GetString(2));
                    }
                }
            }
            finally
            {
                Console.WriteLine("finally disposing of the connection");
                if (sqlConnection.State == System.Data.ConnectionState.Open)
                    sqlConnection.Close();
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

如果他的消费者没有脱离foreach,那么everthing很好并且读者将返回false,while循环willend并且该函数清除数据库命令和连接.但是如果在我结束之前呼叫者从foreach断开会发生什么呢?

Eri*_*ert 36

好问题.你不必担心这个; 编译器会为你处理它.基本上,我们所做的是将finally块的清理代码放在生成的迭代器上的特殊清理方法中.当控制离开调用者的foreach块时,编译器会生成调用迭代器上的清理代码的代码.

一个简化的例子:

static IEnumerable<int> GetInts()
{
    try { yield return 1; yield return 2;} 
    finally { Cleanup(); }
}
Run Code Online (Sandbox Code Playgroud)

你的问题基本上是"在这种情况下调用了清理()吗?"

foreach(int i in GetInts()) { break; }
Run Code Online (Sandbox Code Playgroud)

是.迭代器块是作为一个类生成的,带有调用Cleanup的Dispose方法,然后生成foreach循环,类似于:

{
  IEnumerator<int> enumtor = GetInts().GetEnumerator();
  try
  {
    while(enumtor.MoveNext())
    {
      i = enumtor.Current;
      break;
    }
  }
  finally
  {
    enumtor.Dispose();
  }
}
Run Code Online (Sandbox Code Playgroud)

因此,当突破发生时,最终接管并调用处理器.

如果您想了解我们在此功能设计中考虑的一些奇怪角落案例的更多信息,请参阅我最近的系列文章.

http://blogs.msdn.com/ericlippert/archive/tags/Iterators/default.aspx