在Blazor中,通过IDisposable接口释放托管资源的固定套路(同步方式)

73 阅读2分钟

一、观察下面的代码

private DotNetObjectReference<Counter>? _objRef = null;
private bool disposedValue = false;

private async Task SendToJavaScript2Async(MouseEventArgs e)
{
    var moudle = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./Components/Pages/Counter.razor.js");
    if (null != moudle)
    {
        if (_objRef == null)
        {
            _objRef = DotNetObjectReference.Create(this);
        }
        await moudle.InvokeVoidAsync("SendToJavascript2Async", H1Element, _objRef);
    }
}

_objRef 被传入到js模块里面去了,脱离了C#的垃圾回收机制,因此需要主动释放这个资源。

二、一般的套路

  1. 继承IDisposable接口
  2. 实现接口IDisposable里面的函数Dispose,但是函数Dispose并不写具体的释放操作,而是写一个调用virtual的Dispose的函数。目的是:如果子类再继承自己,子类可以重写这个虚函数,并释放子类,然后子类再调用父类的虚函数,这样实现彻底的释放资源。

三、Blazor的样例代码

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;

namespace BlazorAppJavaScript.Components.Pages
{
    public partial class Counter : IDisposable
    {
        private int currentCount = 0;

        private void IncrementCount()
        {
            currentCount++;
        }

        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            await base.OnAfterRenderAsync(firstRender);
            if (firstRender)
            {
                //第一个是函数名称;第二个和第三个是参数
                //await jsRuntime.InvokeVoidAsync("$.test1","1","2");
            }
        }

        private async Task ChangeTitleAsync(MouseEventArgs e)
        {
            var moudle = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./Components/Pages/Counter.razor.js");
            if (null != moudle)
            {
                await moudle.InvokeVoidAsync("test2", "暴风影音");
                // 这句话很关键,通知界面去更新变量的值(否则变量还是原来的0)
                //await this.InvokeAsync(this.StateHasChanged);
            }
        }
        private async Task CallAsyncFunction(MouseEventArgs e)
        {
            var moudle = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./Components/Pages/Counter.razor.js");
            if (null != moudle)
            {
                var rst = await moudle.InvokeAsync<bool>("getPageSizeAsync");
                if (rst)
                    currentCount += 23;
                else
                    currentCount += 3;
                //按钮本身就会调用await this.InvokeAsync(this.StateHasChanged);
                //这里不用调用,否则死锁
                //await this.InvokeAsync(this.StateHasChanged);
            }
        }

        private ElementReference H1Element { get; set; }


        private async Task SendToJavaScriptAsync(MouseEventArgs e)
        {
            var moudle = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./Components/Pages/Counter.razor.js");
            if (null != moudle)
            {
                await moudle.InvokeVoidAsync("SendToJavascript", H1Element);
            }
        }

        private DotNetObjectReference<Counter>? _objRef = null;
        private bool disposedValue = false;

        private async Task SendToJavaScript2Async(MouseEventArgs e)
        {
            var moudle = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./Components/Pages/Counter.razor.js");
            if (null != moudle)
            {
                if (_objRef == null)
                {
                    _objRef = DotNetObjectReference.Create(this);
                }
                await moudle.InvokeVoidAsync("SendToJavascript2Async", H1Element, _objRef);
            }
        }
        //
        private async Task StaticAsync(MouseEventArgs e)
        {
            var module = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./Components/Pages/Counter.razor.js");
            if (null != module)
            {
                await module.InvokeVoidAsync("StaticJavascriptAsync");
            }

        }

        private static readonly int[] result = [1,2,3,4,5];

        [JSInvokable("foo")]
        public static Task<int[]> ReturnArray()
        {
            return Task.FromResult(result);
        }

        [JSInvokable]
        public void Update()
        {
            this.currentCount += 100;
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)
                    if (this._objRef != null)
                    {
                        this._objRef.Dispose();
                        this._objRef = null;
                    }
                }

                // TODO: 释放未托管的资源(未托管的对象)并重写终结器
                // 释放未托管的资源,如例如打开的文件句柄等
                // TODO: 将大型字段设置为 null
                disposedValue = true;
            }
        }

        // 如果有非托管资源(如 IntPtr、原生库句柄等),才需要析构函数
        // 例如:private IntPtr nativeHandle; // 非托管资源	
        // 在这种情况下,即使用户没有调用 Dispose(),GC 在回收对象时,也会通过析构函数调用 Dispose(false) 来释放非托管资源。
        ~Counter()
        {
            // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
            Dispose(disposing: false);
        }

        public void Dispose()
        {
            // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }
    }
}