用于取消订阅CustomControls中事件的模式

时间:2014-07-28 22:27:25

标签: c# wpf events memory-leaks windows-runtime

我正在开发一个自定义控件,它在内部订阅了Touch.FrameReported - 一个静态事件。这有可能导致内存泄漏(在某些情况下,确实如此)。

这是我目前的解决方案。订阅/取消订阅已加载/已卸载事件。但是,我发现并不总是调用Unloaded事件。这可能导致内存泄漏。

// Imagine this is a CustomControl, to be consumed by users 
// with no regard for calling Dispose
public class CustomGrid : Grid
{   
    public CustomGrid()
    {
        Loaded += (s, a) =>
                  {
                      Touch.FrameReported -= OnTouchFrameReported;
                      Touch.FrameReported += OnTouchFrameReported;
                  };

        Unloaded += (s, a) =>
                  {
                    // The intention is to unsubscribe on unload, which should pre-date
                    // user intended 'disposal' of the control
                    Touch.FrameReported -= OnTouchFrameReported;
                  };
    }

是否有一种已知的模式可以解决这个问题?取消订阅自定义控件中的事件'拆卸'?我已经尝试过了:

  • 取消订阅卸载。并不总是被召唤。
  • 进行处理。无法使用,因为用户可能无法确定地调用Dispose
  • Weak Events。很好,但许多实现不适合WinRT / Silverlight,或者,它们需要显式注销,或者,如果事件被调用,它们只会取消注册(Duh!它是弱事件)!
  • 终结。如果像事件处理程序这样的GC根源,是否会阻止终结器?

1 个答案:

答案 0 :(得分:1)

tl; dr如果您使用#1(卸载时取消订阅)和#3(弱事件监听器)的组合,那么我不认为您的控件应该因任何内存泄漏而出错。你无能为力。

实施IDisposable并没有真正帮助,因为没有人想要打电话" Dispose"在UI元素上,无论如何,在那些" Unloaded"没有被叫,要求" Dispose"只会把罐头踢到路上。而且你是对的,如果有一个静态事件作为其调用列表的一部分持有你的控件,则不会调用终结器。

我的理解是"卸载"从Visual Tree中删除控件时应该调用。所以在"卸载"没有触发它肯定应该在哪里,然后在某个框架控件中有一个错误(which does seem to be a possibility),或者你的用户有一个错误'阻止您的控件容器被卸载的代码。在任何一种情况下,您的控件都不会成为内存泄漏的来源。

使用弱事件处理程序可能是一个很好的故障保护 - 它允许您的控件成为GC,如果对它的唯一引用是弱事件侦听器(因此,这将阻止您的" FrameReported& #34;监听器导致内存泄漏)。我理解你关于实现的观点 - 实现起来似乎很棘手,但原则上这个技术没有任何问题(你可能知道,框架使用它作为绑定的事件监听器)。