C # Обеспечение изящного завершения метода итератора - PullRequest
3 голосов
/ 18 января 2012

Я протестировал этот блок кода и обнаружил, что метод GetInts не выходит из метода и выводит «GetInts отключен», как я и ожидал, традиционно.Я хочу написать элемент прокрутки, который постепенно загружает datarows из базы данных с возвращаемым доходом, но я не уверен в правильности метода.

С другой стороны, перенос блока return 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");
        }
    }
}

Ответы [ 2 ]

2 голосов
/ 18 января 2012

Когда я запускаю его, отключает :

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 элементов; печать отключена» - он не будет печатать отключенный, если он сначала не выполнил все операции. Если вы используете 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() итератора.

Кроме того, _mIter равно IEnumerator<T>; он явно реализует IDisposable, и вы несете ответственность за вызов Dispose() в какой-то момент. Сделай это, и все будет работать. Даже с IEnumerator (не универсальным, не явно IDisposable) вы должны следовать тому же подходу, что и компилятор, и проверить , если перечислитель IDisposable, и вызвать Dispose() соответственно.

Поскольку вы не используете данные в одном блоке, вы не можете использовать using, но вы все равно должны их утилизировать. Это может означать, что ваш агрегат типа IDisposable и передача вызова. Кроме того, вы можете явно удалить и освободить перечислитель, когда достигнете конца. Я не могу изменить ваш код, чтобы проиллюстрировать это, так как это не имеет смысла для static данных, но я надеюсь, что перечислитель не будет static в реальном коде в любом случае.

2 голосов
/ 18 января 2012

Вы утверждаете, что он не "отключает соединение с базой данных" - но код, который вы показали , выводит и выводит "DataSource отключен" ... так что, насколько я вижу, он работает нормально.

Да, вам нужно убедиться, что что-то вызывает Dispose на итераторе - что обычно следует делать либо с помощью оператора using, либо (чаще) путем итерации поIEnumerable с циклом foreach.

Вы не можете гарантировать, что блок итератора будет выполняться до самого завершения, но любые соответствующие 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");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...