C# 确保迭代器方法正常完成

发布于 2024-12-27 09:15:56 字数 2101 浏览 2 评论 0原文

我测试了这段代码,发现 GetInts 方法不会像我所期望的那样退出该方法并打印“GetInts已断开连接”。我想编写一个滚动控件,以增量方式从数据库下载数据行并返回,但我不确定正确的方法。

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

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

I tested this block of code and find that the GetInts method does not exit the method and print "GetInts disconnected" as i would expect, traditionally. I want to write a scroll control that incrementally downloads datarows from the database with yield return, but I am unsure of the correct method.

On the other hand, wrapping the yield return block with using block will guarantee the call on dispose(), but should I go that way?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

烟雨扶苏 2025-01-03 09:15:56

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

Scroll()
GetInts connected
yield 99
99
...snip...
yield 90
90
Scroll() completed
DataSource Disconnected
Program End

请注意,如果您读到数据末尾,它断开自身连接;然而,您要求 100 个整数,并滚动浏览其中的 10 个;就迭代器而言,您已将其挂起 10%。如果您耗尽迭代器块,它会清除任何 using 等;如果您没有耗尽它,则需要显式处置它。您可以通过将其更改为 GetInts(5) 来说明这一点(使所有其他代码保持不变):

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已断开连接”的原因是......它永远不会到达那里,除非你耗尽它!这就是你的代码所说的:“打印已连接;产生 i items;打印断开连接” - 它不会打印断开连接,除非它首先完成了所有的产量。如果您使用 finally (再次使用 GetInts(100)),那么情况会发生变化:

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

然后它就可以工作了:

...
yield 90
90
Scroll() completed
DataSource Disconnected
GetInts disconnected
Program End

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

此外,_mIterIEnumerator;它显式实现了IDisposable,并且您负责在某个时刻调用Dispose()。这样做,一切都会成功。即使使用 IEnumerator (非泛型,非显式 IDisposable),您也应该遵循与编译器相同的方法,并检查枚举数是否为< code>IDisposable,并相应地调用 Dispose()

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

When I run it, it does disconnect:

Scroll()
GetInts connected
yield 99
99
...snip...
yield 90
90
Scroll() completed
DataSource Disconnected
Program End

Note that it would disconnect itself if you were reading to the end of the data; however, you are asking for 100 ints, and scrolling through 10 of them; as fas as the iterator is concerned you've left it hanging 10% in. If you exhaust an iterator block, it cleans up any using etc; if you don't exhaust it, it needs to be explicitly disposed. You can illustrate this by changing it to GetInts(5) (leaving all other code the same):

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

The reason it doesn't show "GetInts disconnected" otherwise is that ... it never gets there unless you exhaust it! That is what your code says: "print connected; yield i items; print disconnected" - it won't print disconnected unless it has first done all the yields. If you use a finally though (using the GetInts(100) again), then this changes:

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

Then it works:

...
yield 90
90
Scroll() completed
DataSource Disconnected
GetInts disconnected
Program End

Basically, finally is mapped into the Dispose() of the iterator.

Additionally, _mIter is IEnumerator<T>; it explicitly implements IDisposable, and you are responsible for calling Dispose() at some point. Do that, and it will all work. Even with IEnumerator (non-generic, not explicitly IDisposable) you should follow the same approach as the compiler, and check if the enumerator is IDisposable, and call Dispose() accordingly.

Since you aren't consuming the data in one chunk, you can't use using, but you must still dispose it. This might mean making your type implement IDisposable, and passing along the call. Also, you might want to explicitly dispose and release the enumerator when you reach the end. I can't really change your code to illustrate this, as it doesn't make sense on static data, but I would hope that an enumerator would not be static on real code anyway.

伴随着你 2025-01-03 09:15:56

您声称它不会“断开数据库连接” - 但您显示的代码确实打印出“数据源已断开连接”......所以据我所知,它工作正常。

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

您不能保证迭代器块本身会运行完成,但任何适当的finally在迭代器被释放时执行。

编辑:因此,如果您想确保看到“GetInts已断开连接”,您可以将其放在 finally 块中:

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

You claim that it doesn't "disconnect the database connection" - but the code you've shown does print out "DataSource disconnected"... so it's working fine as far as I can see.

Yes, you need to make sure that something calls Dispose on the iterator - which should normally be done either with a using statement or (more usually) by iterating over an IEnumerable with a foreach loop.

You can't guarantee that the iterator block will run to completion itself, but any appropriate finally blocks will be executed when the iterator is disposed.

EDIT: So if you want to make sure you'll see "GetInts disconnected" you can just put that in a finally block:

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");
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文