我们的UI的一部分使用IObservableElementEnumerable.EnumerableChanged以便更新用户,例如从文件夹中删除域对象。
当处理用户界面时,我们取消订阅活动...或者我们认为。事实证明,取消订阅没有任何效果,我们的事件处理程序仍然被调用。这会导致许多奇怪的错误,但也会导致内存泄漏。
unsubscription唯一可行的方法是,如果我们存储IObservableElementEnumerable引用,而不是再次调用IObservableElementEnumerableFactory.GetEnumerable(obj)。但反过来,这可能会保留对文件夹对象的实时引用,如果用户删除了文件夹本身,它将会中断。
这尤其令人费解,因为GetEnumerable()文档明确指出:“预计使用相同域对象的后续调用将产生相同的IObservableElementEnumerable实例。”这不能被解释为保证吗?
取消订阅是否有任何理由不起作用?
以下代码复制了Petrel 2011上的问题(添加到带有菜单扩展名的简单插件,或获取完整解决方案here(DropBox)):
using System;
using System.Linq;
using System.Windows.Forms;
using Slb.Ocean.Core;
using Slb.Ocean.Petrel;
using Slb.Ocean.Petrel.Basics;
using Slb.Ocean.Petrel.UI;
namespace ObservableElementEnumerable
{
public class OEEForm : Form
{
private Droid _droid;
private bool _disposed;
public OEEForm()
{
IInput input = PetrelProject.Inputs;
IIdentifiable selected = input.GetSelected<object>().FirstOrDefault() as IIdentifiable;
if (selected == null)
{
PetrelLogger.InfoOutputWindow("Select a folder first");
return;
}
_droid = selected.Droid;
GetEnumerable().EnumerableChanged += enumerable_EnumerableChanged;
PetrelLogger.InfoOutputWindow("Enumerable subscribed");
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing && !_disposed)
{
GetEnumerable().EnumerableChanged -= enumerable_EnumerableChanged;
PetrelLogger.InfoOutputWindow("Enumerable unsubscribed (?)");
_droid = null;
_disposed = true;
}
}
IObservableElementEnumerable GetEnumerable()
{
if (_disposed)
throw new ObjectDisposedException("OEEForm");
object obj = DataManager.Resolve(_droid);
IObservableElementEnumerableFactory factory = CoreSystem.GetService<IObservableElementEnumerableFactory>(obj);
IObservableElementEnumerable enumerable = factory.GetEnumerable(obj);
return enumerable;
}
void enumerable_EnumerableChanged(object sender, ElementEnumerableChangeEventArgs e)
{
PetrelLogger.InfoOutputWindow("Enumerable changed");
if (_disposed)
PetrelLogger.InfoOutputWindow("... but I am disposed and unsubscribed!");
}
}
public static class Menu1
{
public static void OEEBegin1_ToolClick(object sender, System.EventArgs e)
{
OEEForm f = new OEEForm();
f.Show();
}
}
}
要复制:
消息日志应该清楚地表明在处理表单后仍然会调用事件处理程序。
答案 0 :(得分:3)
您已通过连接事件保留对基础可枚举的引用。事件也是参考。只需保留对可枚举的引用,并取消订阅与您订阅的实例相同的实例。
要处理用户删除的对象问题,您需要收听删除事件。