C# 确保迭代器方法正常完成
我测试了这段代码,发现 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
当我运行它时,它确实断开连接:
请注意,如果您读到数据末尾,它会断开自身连接;然而,您要求 100 个整数,并滚动浏览其中的 10 个;就迭代器而言,您已将其挂起 10%。如果您耗尽迭代器块,它会清除任何
using
等;如果您没有耗尽它,则需要显式处置它。您可以通过将其更改为GetInts(5)
来说明这一点(使所有其他代码保持不变):它不显示“GetInts已断开连接”的原因是......它永远不会到达那里,除非你耗尽它!这就是你的代码所说的:“打印已连接;产生
i
items;打印断开连接” - 它不会打印断开连接,除非它首先完成了所有的产量。如果您使用finally
(再次使用GetInts(100)
),那么情况会发生变化:然后它就可以工作了:
基本上,
finally
被映射到迭代器的Dispose()
中。此外,
_mIter
是IEnumerator
;它显式实现了IDisposable
,并且您负责在某个时刻调用Dispose()
。这样做,一切都会成功。即使使用IEnumerator
(非泛型,非显式IDisposable
),您也应该遵循与编译器相同的方法,并检查枚举数是否为< code>IDisposable,并相应地调用Dispose()
。由于您没有使用一个块中的数据,因此您无法使用
using
,但您仍然必须处置它。这可能意味着让您的类型实现IDisposable
,并传递调用。此外,您可能希望在到达末尾时显式处置并释放枚举器。我无法真正更改您的代码来说明这一点,因为它对静态数据没有意义,但我希望枚举器在实际中不会是静态的无论如何,代码。When I run it, it does disconnect:
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 toGetInts(5)
(leaving all other code the same):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 afinally
though (using theGetInts(100)
again), then this changes:Then it works:
Basically,
finally
is mapped into theDispose()
of the iterator.Additionally,
_mIter
isIEnumerator<T>
; it explicitly implementsIDisposable
, and you are responsible for callingDispose()
at some point. Do that, and it will all work. Even withIEnumerator
(non-generic, not explicitlyIDisposable
) you should follow the same approach as the compiler, and check if the enumerator isIDisposable
, and callDispose()
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 implementIDisposable
, 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 onstatic
data, but I would hope that an enumerator would not bestatic
on real code anyway.您声称它不会“断开数据库连接” - 但您显示的代码确实打印出“数据源已断开连接”......所以据我所知,它工作正常。
是的,您需要确保某些东西在迭代器上调用
Dispose
- 通常应该使用using
语句来完成,或者(更常见的是) )通过使用foreach
循环迭代IEnumerable
。您不能保证迭代器块本身会运行完成,但任何适当的
finally
块将在迭代器被释放时执行。编辑:因此,如果您想确保看到“GetInts已断开连接”,您可以将其放在
finally
块中: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 ausing
statement or (more usually) by iterating over anIEnumerable
with aforeach
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: