2012-06-06 38 views
3

我们的UI的部分使用IObservableElementEnumerable.EnumerableChanged为了更新,如果用户例如从文件夹中删除一个域对象。Unsubscribe from IObservableElementEnumerable.EnumerableChanged does not work?

当UI被处置时,我们退订事件...或者我们认为。事实证明,取消订阅没有任何效果,我们的事件处理程序仍然被调用。这造成了一些奇怪的错误,但也导致了内存泄漏。

退订工作的唯一时间,是如果我们存储IObservableElementEnumerable引用而不是再次调用IObservableElementEnumerableFactory.GetEnumerable(obj)。但是,这反过来可能会保留对文件夹对象的实时引用,如果文件夹本身被用户删除,则该文件夹对象将会中断。

由于GetEnumerable()文档明确指出:“期望使用相同域对象的后续调用将产生IObservableElementEnumerable的同一实例,这尤其令人费解。这是不是被解释为保证?

应该有任何理由退订不起作用?

以下代码复制于2011年海燕问题(添加到一个简单的插件与菜单扩展,或获取完整的解决方案here(投放箱)):

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. 使用插件运行海燕
  2. 使用包含对象的文件夹加载项目
  3. 选择文件夹
  4. 激活插件菜单项
  5. 随着弹出打开,文件夹
  6. 中删除对象关闭窗体弹出
  7. 文件夹

消息日志应该清楚地显示在删除对象事件处理程序在表单处理后仍然被调用。

回答

3

您已经通过连接事件保留对底层枚举的引用。事件也是参考。只需保留对您订阅的同一实例的可枚举和取消订阅的引用即可。

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

+0

嗯......不,订阅一个事件并不会让* my *代码引用这个枚举,它会将事件引用添加到我的事件处理函数中。我需要重新审视这个问题。这个代码涉及到Petrel项目中的整个子层次结构,如果你的建议是正确的,那么它将成为一个难以处理所有事件和可枚举项目的工作。 –

+0

而且你似乎暗示SDK中的提示(即对GetEnumerable()的后续调用返回相同的实例)是不正确的,对吧? –