我测试了这段代码,发现GetInts方法没有退出方法并按照我的预期打印“GetInts disconnected”,传统上。我想编写一个滚动控件,使用yield return逐步从数据库中下载数据行,但我不确定正确的方法。
另一方面,使用block包装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");
}
}
}
答案 0 :(得分:2)
您声称它没有“断开数据库连接” - 但您显示的代码打印出“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");
}
}
答案 1 :(得分:2)
当我运行它时, 断开连接:
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
。