处置迭代器块的参数 [英] Disposing of arguments for an iterator block

查看:77
本文介绍了处置迭代器块的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好吧,这里有一段不好的代码:

Allright, here it goes a good piece of bad code:

public class Log : CachingProxyList<Event> {
    public static Log FromFile(String fullPath) {
        using (FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read)) {
            using (StreamReader sr = new StreamReader(fs)) {
                return new Log(sr);
            }
        }
    }
    public Log(StreamReader stream)
        : base(Parser.Parse(Parser.Tokenize(stream))) {
        /* Here goes some "magic", the whole reason for this
         * class to exist, but not really relevant to the issue */
    }
}

现在出现了一些问题:

CachingProxyList IEnumerable< T> 的实现,该实现提供了自定义的缓存枚举器:它使用 IEnumerable< T 在其构造函数上,并在开始时对其进行枚举,但将每个项目保存在私有的 List< T> 字段中,因此在进行进一步迭代之前,继续进行实际的解析(而不是一次又一次地解析;或者不必解析庞大的日志以查询其中的一小部分)。

请注意,此优化实际上是必要的,并且大多数已经在工作(如果我删除了使用语句,除泄漏的文件句柄外,其他一切都很好)。

CachingProxyList is an implementation of IEnumerable<T> that provides a custom "caching" enumerator: it takes an IEnumerable<T> on its constructor, and initially enumerates through it, but saves each item on a private List<T> field so further iterations run through that before going on with the actual parsing (rather than parsing every now and again; or having to parse a huge log just to query a small portion of it).
Note that this optimization was actually needed, and most of it is already working (if I remove the using statements, everything goes fine except for the leaking file handles).

Parse Tokenize 是迭代器块(AFAIK,这是我可以同时推迟执行和清除代码的唯一明智的方法);它们的签名是 IEnumerable< Event>。解析(IEnumerable< Token>) IEnumerable< Token> Tokenize(StreamReader)。他们的逻辑与问题无关。

Both Parse and Tokenize are iterator blocks (AFAIK, the only sane way I could have deferred execution and clean code at the same time); their signatures are IEnumerable<Event> Parse(IEnumerable<Token>) and IEnumerable<Token> Tokenize(StreamReader). Their logics are unrelated to the issue.

逻辑流程很明确;代码各部分的意图相当明显;但是那些 using 块不能与整个推迟执行的事情相处(在我通过 Log 对象,使用已被退出并丢弃了流,因此 Tokenize 尝试从中读取

The logical flow is quite clear; and the intent of each part of the code rather obvious; but those using blocks don't get along with the whole deferred execution thing (by the time I'm enumerating through my Log object, the using have already been exited and the stream disposed, so Tokenize's attempts to read from it miserably crash).

我可以承受较长时间的文件锁定(开放流),但是迟早我不得不关闭它。由于我不能真正使用 ,因此必须显式处理流。

I can afford having a lock on the file (the open stream) for a relatively long time, but sooner or later I'll have to close it. Since I can't really use the usings, I'll have to explicitly dispose of the streams.

问题:我应该在哪里调用 Dispose()?有没有常见的成语来应对此类情况?我不想以旧方式来执行此操作(在多个地方释放资源,每次在某个地方的少量代码更改时都必须审查每个发行版,依此类推。)

The question is: where should I put the calls to Dispose()? Is there any common idiom to deal with scenarios like these? I wouldn't like to do this the "old way" (releasing resources at several places, having to review each release anytime a tiny bit of the code changes somewhere, and so on).

我的第一个想法是使 Log 类成为可处理的,因此其构造函数可以采用文件名,并具有该类内的所有资源管理(仅需要使用者在完成后自行处理 Log 本身),但是在调用 base 构造函数(产生该构造函数参数的调用需要流。)

My first idea was making the Log class disposable, so its constructor could take a file-name and have all the resource-management within the class (requiring only the consumer to dispose of the Log itself when done), but I can see no way of creating and saving the stream before calling the base constructor (the stream is required for the calls that yield the argument for that constructor).

注意: CachingProxyList (我想使其具有足够的通用性以使其可重用)。特别是,构造函数对于强制实施其余实现依赖的一些不变量至关重要(例如内部枚举器对象永远不会为null)。

Note: the CachingProxyList shouldn't be touched unless strictly needed (I want to keep it generic enough to make it reusable). Specially, the constructor is essential to enforce some invariants the rest of the implementation heavily relies in (such as the internal enumerator objects never being null). Everything else, OTOH, should be fair game.

感谢您的耐心等待,如果您已读过本文,也要感谢您提供的任何帮助 ;)

Thanks for your patience if you have read this, and also thanks in advance for any help provided ;).

推荐答案


  • 封装非托管资源的类需要实现处置模式( IDisposable 接口)。例如流,数据库连接等

  • 每个资源必须具有一个所有者

  • 所有者负责呼叫<$资源上的c $ c> Dispose()

  • 如果所有者无法立即调用 Dispose()它的资源或不知道何时调用它,那么它需要自己实现 IDisposable 接口并调用 Dispose()

    • Classes that encapsulate unmanaged resources need to implement dispose pattern (IDisposable interface). For example stream, database connection, etc
    • Every resource must have one owner
    • Owner is responsible for calling Dispose() on the resource
    • If owner cannot immediately call Dispose() on its resource or does not know when to call it, then it needs to implement IDisposable interface itself and call Dispose() on its resource in there.
    • 上面的语句可能有例外,但这是一般规则。示例是 StreamWriter ,它接受一个流(实现 IDisposable 接口),并强制其实现 IDisposable 接口本身-,因为它不知道何时处置它。

      Above statements could have exceptions but that is the general rule. Example is StreamWriter which takes in a stream (which implement IDisposable interface) and that forces it to implement IDisposable interface itself - since it does not know when to dispose it.

      你的情况。您的班级使用了可抛弃的资源,但它不知道何时处置它-或者我认为这是。这将使其实现 IDisposable 接口。您的 Log 类的客户必须在您的类上调用 Dispose()

      It is the same in your case. Your class uses a disposable resource while it does not know when to dispose it - or that is what I assume. This would make it to implement IDisposable interface. Client of your Log class will have to call Dispose() on your class.

      因此,您将看到这变成了一条链,而非一次性客户端将不得不对其使用的资源进行调用处置,而该资源将对其资源进行处置,等等。

      So as you can see, this becomes a chain while the non-disposable client will have to call dispose on the resource it uses and that resource will dispose its resource, etc...

      这篇关于处置迭代器块的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆