如何在我的 Dispose() 中正确处理 IDisposable 项目列表?

问题描述 投票:0回答:1

我有一堂这样的课:

public class MyFolderRefresher {
    public async Task OnRename(RenamedEventArgs e) {
        // do something
    }
}

public class MyDisposableListOfFileSystemWatchers : IDisposable {
    private List<FileSystemWatcher> _mWatchers;
    private MyFolderRefresher _mFolderRefresher;

    public MyDisposableListOfFileSystemWatchers(List<string> pathsToWatch) {
        _mFolderRefresher = new MyFolderRefresher();
        _mWatchers = new List<FileSystemWatcher>();

        foreach (var path in pathsToWatch) {
            var fileSystemWatcher = new FileSystemWatcher(path)
            {
                NotifyFilter = NotifyFilters.DirectoryName,
                IncludeSubdirectories = true,
                InternalBufferSize = 64 * 1024
            };

            // Listen to events in some different class
            fileSystemWatcher.Renamed += async (sender, e) => await _mFolderRefresher.OnRename(e);
            
            _mWatchers.Add(fileSystemWatcher);
        }
    }

    public void Dispose() {
        Dispose(true);
    }

    private bool _mDisposed = false;
    protected virtual void Dispose(bool disposing) {
        if (_mDisposed) {
            return;
        }

        if (disposing) {
            foreach(var watcher in _mWatchers) {
                watcher.Dispose();
            }

            _mWatchers.Clear();
        }

        _mDisposed = true;
    }
}

问题是,当调用 MyDisposableListOfFileSystemWatchers 类的

Dispose()
时,我有时会得到
System.InvalidOperationException
。大多数时候,它只是事件查看器中的堆栈跟踪,但是一旦我在调试器中捕获它并能够看到它是
System.InvalidOperationException: Collection was modified; enumeration operation may not execute
(不确定两者是否是同一个问题)。

我想知道我的 Dispose() 中是否做错了什么。具体来说:

  • 我应该用我的
    GC.SuppressFinalize(this)
    打电话给
    public void Dispose()
    吗?
  • 我可以处理正在迭代的对象吗?
  • 我可以清除集合吗,还是应该将其留给垃圾收集器?
  • 既然我正在处置发布者(FileSystemWatcher),我是否正确地假设我不需要手动取消订阅事件?

我已阅读文档,但无法找到这些问题的明确答案,并且当涉及到

System.InvalidOperationException
时我有点不知所措,因为我不明白如何可能构造函数和 Dispose 可以同时运行(这些是唯一修改 _mWatchers` 的地方)。

c# idisposable filesystemwatcher
1个回答
0
投票

我应该在 public void Dispose() 中调用 GC.SuppressFinalize(this) 吗?

不,你没有终结器,没有终结器就没有意义。

问题就变成了,你应该有一个终结器吗?如果有人从您的类派生,并且还拥有一些需要清理的本机资源,则需要这样做。如果这不是您打算做的事情,我建议您将您的课程标记为

sealed
。这允许您简化您的 dispose 方法,并将实现完整 dispose-with-finalizer-pattern 的责任(参见最后一个示例)转移给任何解封您的类的人。

我可以处理正在迭代的对象吗?

是的,完全没问题。

我可以清除集合吗,还是应该将其留给垃圾收集器?

这应该没什么大不了的。清除它应该不会造成伤害,并且在某些情况下可能会有所帮助。

既然我正在处置发布者(FileSystemWatcher),我是否正确地假设我不需要手动取消订阅事件?

也许吧。无论如何,我可能会这样做,但在处理方面我有点偏执。

System.InvalidOperationException:集合被修改;枚举操作可能无法执行

我看不出这可能是由发布的代码引起的。 FileSystemWatcher.Dispose 的文档也没有提到任何异常的可能性。

您没有在文件系统观察器上设置任何

SynchronizingObject
。这意味着事件将在后台线程上引发,使您的程序成为多线程的。这意味着您需要非常小心以确保线程安全。例如,如果重命名事件处理程序以某种方式最终处置了您的对象,则该对象可能会在其构造函数完成之前被处置。这可能会导致列表在迭代时被修改。

如果是这种情况,您可能需要以某种方式消除这种可能性。您还可以在构造函数的末尾设置一个布尔值,并在您的 dispose 方法中添加一个

Debug.Assert(IsFullyConstructed)
来帮助确认或排除这个理论。

© www.soinside.com 2019 - 2024. All rights reserved.