取消订阅IObservableElementEnumerable.EnumerableChanged不起作用?

时间:2012-06-06 21:15:00

标签: ocean

我们的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();
    }
  }
}

要复制:

  1. 使用插件运行Petrel
  2. 使用包含对象的文件夹加载项目
  3. 选择文件夹
  4. 激活插件菜单项
  5. 弹出窗口,删除文件夹
  6. 中的对象
  7. 关闭弹出表格
  8. 删除文件夹
  9. 中的对象

    消息日志应该清楚地表明在处理表单后仍然会调用事件处理程序。

1 个答案:

答案 0 :(得分:3)

您已通过连接事件保留对基础可枚举的引用。事件也是参考。只需保留对可枚举的引用,并取消订阅与您订阅的实例相同的实例。

要处理用户删除的对象问题,您需要收听删除事件。