You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin.Forms/MAUI自定义控件IDisposable:Dispose()何时被调用?

Xamarin.Forms/.NET MAUI 自定义控件 IDisposable.Dispose() 调用时机与实践

一、Dispose() 的触发逻辑

Dispose() 是 .NET 用于确定性释放托管/非托管资源的接口方法,它的调用时机和垃圾回收(GC)紧密相关:

  • 只有当自定义控件对象被 GC 标记为可回收,且你严格实现了标准 IDisposable 模式时,Dispose() 才会被执行。GC 回收是异步且不确定的,不会在你移除页面、弹出导航栈后立刻触发。
  • 框架层面,Xamarin.Forms/.NET MAUI 只会主动处理 Page 等顶级元素的生命周期,自定义 ContentView 这类子控件的 Dispose 不会被框架自动调用,需要你关联到合适的生命周期事件。

二、为什么你的测试场景没触发?

你遇到的情况大概率是以下原因:

  • 存在隐式引用泄漏:比如自定义控件订阅了外部对象的事件却未取消、绑定的命令仍持有控件引用,这些都会让 GC 认为对象还在被使用,无法回收,自然不会执行 Dispose()。
  • 调试环境的 GC 延迟:调试模式下,.NET 会延迟 GC 回收以避免打断调试流程,导致你看不到 Dispose() 被调用。
  • 未实现标准 IDisposable 模式:如果只简单实现了 IDisposable 接口的 Dispose() 方法,没有配合终结器和 Dispose(bool) 重载,GC 可能无法正确触发资源释放逻辑。

三、正确的处理方式(无需手动直接调用 Dispose())

不需要手动调用 Dispose(),但要确保它能在合适时机被触发,核心是做好两点:

  1. 严格实现标准 IDisposable 模式
    这是确保资源被正确释放的基础,示例代码:
    public class MyCustomControl : ContentView, IDisposable
    {
        private bool _disposed = false;
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed) return;
    
            if (disposing)
            {
                // 释放托管资源:取消事件订阅、解绑绑定、清理命令引用等
                SomeExternalEvent -= OnExternalEventTriggered;
                MyBindingContext?.UnsubscribeFromEvents();
            }
    
            // 释放非托管资源(如果有,比如原生句柄)
    
            _disposed = true;
        }
    
        ~MyCustomControl()
        {
            Dispose(false);
        }
    }
    
  2. 关联控件的生命周期事件
    在自定义控件的构造函数中订阅 Unloaded 事件,当控件从视觉树移除时触发 Dispose():
    public MyCustomControl()
    {
        InitializeComponent();
        Unloaded += OnControlUnloaded;
    }
    
    private void OnControlUnloaded(object sender, EventArgs e)
    {
        Dispose();
        Unloaded -= OnControlUnloaded; // 移除自身引用,避免泄漏
    }
    

四、验证 Dispose() 是否生效的正确方法

  • 切换到发布模式测试,调试模式的 GC 行为会干扰结果。
  • 若需在调试时验证,可以手动触发 GC:
    // 在页面弹出导航栈后执行
    GC.Collect();
    GC.WaitForPendingFinalizers();
    
    此时如果没有引用泄漏,Dispose() 应该会被调用。

内容的提问来源于stack exchange,提问作者camionkraken

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 10:06:22