首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C#确保迭代器方法优雅地结束

C#确保迭代器方法优雅地结束
EN

Stack Overflow用户
提问于 2012-01-18 15:36:44
回答 2查看 240关注 0票数 3

我测试了这段代码,发现GetInts方法并没有像我期望的那样退出该方法并输出"GetInts disconnected“。我想写一个scroll控件,它可以从数据库中增量地下载数据行,并返回that return,但我不确定正确的方法。

另一方面,用using块包装收益返回块将保证对dispose()的调用,但我应该这样做吗?

代码语言:javascript
复制
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IteratorPattern
{
    class Program
    {
        static IEnumerator<int> _mIter;

        static void Main(string[] args)
        {
            // get an enumerator to enumerate values in the database, at a later time
            _mIter = GetInts(100).GetEnumerator();

            // simulate some scrolling, which will add values to my scroll box
            Scroll(10);

            // suppose this control is now redundant,
            // but this does not disconnect the data source 
            _mIter.Dispose();

            // program ends will connection still open?
            Console.WriteLine("Program End");
        }
        // iterate and cache (not implemented) values
        private static void Scroll(int units)
        {
            Console.WriteLine("Scroll()");

            while(units-- != 0 && _mIter.MoveNext())
            {
                Console.WriteLine(_mIter.Current);
            }

            Console.WriteLine("Scroll() completed");
        }
        // connect to database, yield-return each datarow, and disconnect (hopefully)
        static IEnumerable<int> GetInts(int i)
        {
            Console.WriteLine("GetInts connected");

            using (var ds = new DataSourceWrapper())
            {
                while (i-- != 0)
                {
                    Console.WriteLine("yield {0}", i);
                    yield return i;
                }
            }

            // not called! 
            Console.WriteLine("GetInts disconnected");
        }
    }
    // try using a datasource wrapper to ensure Dispose() is called to disconnect the connection.
    public class DataSourceWrapper : IDisposable
    {
        public void Dispose()
        {
            Console.WriteLine("DataSource Disconnected");
        }
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-01-18 15:41:39

当我运行它时,它确实断开了连接:

代码语言:javascript
复制
Scroll()
GetInts connected
yield 99
99
...snip...
yield 90
90
Scroll() completed
DataSource Disconnected
Program End

注意,如果你正在读到数据的末尾,它会断开本身;然而,你要求100个整数,并滚动其中的10个;就迭代器而言,你已经把它挂在了10%的位置上。如果你耗尽了一个迭代器块,它会清除所有的using等;如果你没有耗尽它,它需要被显式地处理掉。您可以通过将其更改为GetInts(5)来说明这一点(保持所有其他代码不变):

代码语言:javascript
复制
Scroll()
GetInts connected
yield 4
4
yield 3
3
yield 2
2
yield 1
1
yield 0
0
DataSource Disconnected
GetInts disconnected
Scroll() completed
Program End

它没有显示"GetInts disconnected“的原因是...除非你耗尽它,否则它永远不会到达那里!这就是你的代码所说的:"print connected;your i items;print disconnected“-它不会打印断开连接,除非它首先完成了所有的输出。但是,如果您使用finally (再次使用GetInts(100) ),则会发生变化:

代码语言:javascript
复制
    static IEnumerable<int> GetInts(int i)
    {
        Console.WriteLine("GetInts connected");
        try
        {
            using (var ds = new DataSourceWrapper())
            {
                while (i-- != 0)
                {
                    Console.WriteLine("yield {0}", i);
                    yield return i;
                }
            }
        }
        finally
        {
            // not called! 
            Console.WriteLine("GetInts disconnected");
        }
    }

然后它就能工作了:

代码语言:javascript
复制
...
yield 90
90
Scroll() completed
DataSource Disconnected
GetInts disconnected
Program End

基本上,finally被映射到迭代器的Dispose()中。

此外,_mIterIEnumerator<T>;它显式地实现了IDisposable,并且您负责在某些时候调用Dispose()。这样做,一切都会好起来的。即使使用IEnumerator (非泛型,而不是显式IDisposable),您也应该遵循与编译器相同的方法,如果枚举器为IDisposable,则检查,并相应地调用Dispose()

由于不是在一个块中使用数据,所以不能使用using,但仍然必须处理它。这可能意味着让您的类型实现IDisposable,并传递调用。此外,您可能希望在到达末尾时显式释放枚举数。我不能真正更改代码来说明这一点,因为它对static数据没有意义,但我希望枚举器在实际代码中不是static

票数 2
EN

Stack Overflow用户

发布于 2012-01-18 15:40:10

您声称它不会“断开数据库连接”--但是您所显示的代码确实会打印出"DataSource disconnect“...因此,据我所知,它工作得很好。

是的,您需要确保在迭代器上调用Dispose -这通常应该通过using语句或(更常见的)通过使用foreach循环迭代IEnumerable来完成。

您不能保证迭代器块自身将运行到完成,但是当迭代器被释放时,任何适当的finally块都将被执行。

编辑:所以如果你想确保看到"GetInts disconnected“,你可以把它放在一个finally块中:

代码语言:javascript
复制
static IEnumerable<int> GetInts(int i)
{
    try
    {
        Console.WriteLine("GetInts connected");

        using (var ds = new DataSourceWrapper())
        {
            while (i-- != 0)
            {
                Console.WriteLine("yield {0}", i);
                yield return i;
            }
        }
    }
    finally
    {
        Console.WriteLine("GetInts disconnected");
    }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8906699

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档