2012-06-06 36 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()的後續調用返回相同的實例)是不正確的,對吧? –