确保在 Dispose 调用了对 EventHandler 的订阅

Make sure a subscription to an EventHandler is called at Dispose

我在构造函数中订阅了一个 EventHandler,并在 Dispose 方法中取消了订阅。确保在 Dispose 取消订阅此事件对我来说很重要,即即使用户忘记调用 Dispose 也会调用它。

我添加了一个解构函数,它应该在用户忘记时调用 Dispose 方法,但是当我在它上面放置一个断点时,它似乎并没有这样做。

// Program.cs
// this scope is on purpose to make sure it's disposed
{
    var client = new DeribitClient(DeribitEndpointType.Productive);
}

Console.ReadLine();

// Client.cs
public sealed class Client : IDisposable
{
    private readonly WebSocketClient _client;

    public Client()
    {
        _client = new WebSocketClient("wss://...");
        _client.MessageReceived += OnMessageReceived;
    }

    public void Dispose()
    {
        _client.MessageReceived -= OnMessageReceived;

        GC.SuppressFinalize(this);
    }

    private void OnMessageReceived(object? sender, MessageReceivedEventArgs e)
    {
    }

    ~Client()
    {
        Dispose();
    }
}

使用工厂

如果您确实需要取消订阅您的活动,则需要调用 Dispose。 (不要使用终结器,因为你没有非托管内存。)

为了确保你的客户得到处置(如果你不能相信人们使用using),那么我建议你使用工厂。

这样做你将能够在依赖注入容器中注册你的工厂,当你的应用程序关闭时,将调用你的工厂的处理,因此你将能够在那里处理你创建的所有客户端.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace MyApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var hostBuilder = CreateHostBuilder(args);
            using var host = hostBuilder.Build();
            host.Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args)
        {
            IHostBuilder hostBuilder = Host.CreateDefaultBuilder(args);
            hostBuilder = hostBuilder.ConfigureServices((services) =>
            {
                services.AddSingleton<ClientFactory>();
                services.AddHostedService<MyHostedService>();
            });
            return hostBuilder;
        }
    }


    internal class MyHostedService : BackgroundService
    {
        private readonly ClientFactory _clientFactory;
        private readonly IHostApplicationLifetime _hostApplicationLifetime;

        /// <summary>
        /// Constructor used by dependency injection.
        /// </summary>
        public MyHostedService(ClientFactory clientFactory, IHostApplicationLifetime hostApplicationLifetime)
        {
            _clientFactory = clientFactory;
            _hostApplicationLifetime = hostApplicationLifetime;
        }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            var client1 = _clientFactory.GetClient();
            // Oups I forgot to dispose my client

            // Self destruction
            Thread.Sleep(1000);
            _hostApplicationLifetime.StopApplication();
            return Task.CompletedTask;
        }
    }

    internal class ClientFactory : IDisposable
    {
        private List<Client> _clients = new List<Client>();

        public void Dispose()
        {
            foreach (var client in _clients)
            {
                client.Dispose();
            }
            _clients.Clear();
        }

        public Client GetClient()
        {
            var client = new Client();
            _clients.Add(client);
            return client;
        }
    }

    internal class Client : IDisposable
    {
        public void Dispose()
        {
            // Unsubscribe your event here
            // ...
            Console.WriteLine("Client has been disposed");
        }
    }
}