我有一个对象,其中包含其他对象的集合。我想通过在基础对象上调用dispose方法来处理它们。集合的dispose方法将清除集合,但它不会处理它包含的单个对象。所以我需要我的基础对象循环并处理每个子对象。最后一个细节是我想确保集合在它包含的单个对象之前被处理掉。
我想我可以通过使用Dispatcher.BeginInvoke和低优先级来获得此行为。首先将它传递给集合的dispose调用,然后循环并处理每个单独的项目。 (伪)代码看起来像这样:
class Foo
{
FooCollection Children;
void Dispose()
{
//unsubscribe from data model events
CurrentDispatcher.BeginInvoke(Children.Dispose, ApplicationIdle, null);
foreach (Foo Child in Children)
{
CurrentDispatcher.BeginInvoke(Child.Dispose, ApplicationIdle, null);
}
}
}
class FooCollection
{
void Dispose()
{
//unsubscribe from data model events
this.Clear();
}
}
我是否在正确的轨道上?或者我只是让自己陷入竞争状态?也就是说,在foreach完成对孩子的处理请求之前,Dispatcher是否可以绕过来调用处理器?是否有更好的方法来获得理想的行为?
值得注意的是,这些类中的每一个都公开事件,并将处理程序挂钩到其他对象中。我的主要目标是确保在调用dispose时优雅地清理所有处理程序。
编辑(8/24): 这些类的实例订阅来自对象的事件,这些事件通常在应用程序的生命周期中持续存在。因此,持久对象维护对每个实例的引用。我试图确保每个实例在不再需要实例时取消订阅持久对象的事件。
编辑2(8/30): 这是视图模型的一部分,它表示从Web服务检索的数据层次结构。从Web服务检索数据的成本很高,因此返回的实际数据对象会被缓存,通常是在应用程序的生命周期中。数据对象实现ICollectionChanged和IPropertyChanged,有问题的视图模型对象订阅这些事件。应用程序的性质使得用户操作可能会导致视图模型在会话期间被丢弃并重新创建多次。数据的性质使得层次结构中通常有数千个节点必须在模型中表示。与这个问题相关的主要问题是:
1)确保丢弃的视图模型取消订阅基础数据模型的事件
2)确保以这样的方式完成循环遍历废弃视图模型的完整层次结构不会明显影响用户使用视图模型的新实例。
我更新了代码示例,以更准确地表示情况。所以问题仍然存在。这个代码是否会给我预期的结果,即在任何对象实际被处置之前,集合及其所有子代将排队等待处理,然后一段时间后线程将在空闲时间开始处理它们?或者它是否会创建一个竞争条件,在我完成循环之前可能会处理该集合?
大多数海报都集中在这样一个事实上,收藏品得到了清理,在所有诚实的清理中,收藏品可能是不必要的,但这有点习惯,我认为这是一个很好的家务管理。
答案 0 :(得分:1)
检查此链接,它对事件处理程序的垃圾收集有一个很好的解释:What best practices for cleaning up event handler references?
这实际上取决于你的事件如何排队。如果您有从其他更持久的对象接收事件的对象,那么它们将继续接收这些事件,直到事件源被GCed。这表明你的dispose方法应该清除你所连接的事件(使用 - =运算符),如果你真的需要回收这个内存(这些对象有多大?你运行它的系统是什么?)你当你完成对象时,我们必须自己调用这个Dispose
方法......
不必清除集合 - 只需确保对您完成的任何对象的所有引用都设置为null。如果不存在引用且不必维护事件处理程序,则GC将收集该对象。
我也不担心调度员。取消订阅活动不应该造成性能问题。
您不会选择何时处置托管对象 - GC会这样做。在处理孩子之前你不能处置一个集合....因为你根本不能处置一个集合!您只能准备托管对象以进行垃圾回收,其方法是
(1)清除事件处理程序和
(2)删除对你完成的对象的所有引用。
GC会处理剩下的事情。
答案 1 :(得分:0)
您将无法保证调度员调用处理的顺序。鉴于此,我认为处理集合中的单个项目然后在集合上调用dispose会更好。
如果在foreach循环完成之前清除了集合,则最终可能会出现异常。
答案 2 :(得分:0)
我建议你再设计一下。请注意,这不是最终的代码,只是为了表明这个想法。
class Bar:IDisposable
{
void Dispose()
{
//do some logic
}
}
class BarCollection:List<Bar>,IDisposable
{
void Dispose()
{
foreach(Bar bar in this){
bar.Dispose();
}
}
}
class Foo
{
BarCollection Children;
void Dispose()
{
CurrentDispatcher.BeginInvoke(Children.Dispose, ApplicationIdle, null);
}
}