亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解C#如何實現一個事件總線

 更新時間:2024年10月30日 09:03:11   作者:小碼編匠  
Event Bus(事件總線)是一種用于在應用程序內部或跨應用程序組件之間進行事件通信的機制,它允許不同的組件通過發(fā)布和訂閱事件來進行解耦和通信,本文給大家介紹了C# 如何實現一個事件總線,需要的朋友可以參考下

使用 C# 實現一個 Event Bus

Event Bus(事件總線)是一種用于在應用程序內部或跨應用程序組件之間進行事件通信的機制。

它允許不同的組件通過發(fā)布和訂閱事件來進行解耦和通信。在給定的代碼片段中,我們可以看到一個使用C#實現的Event Bus。它定義了一些接口和類來實現事件的發(fā)布和訂閱。

首先,我們有兩個基本的約束接口:IEventIAsyncEventHandler<TEvent>。

IEvent是一個空接口,用于約束事件的類型。IAsyncEventHandler<TEvent>是一個泛型接口,用于約束事件處理程序的類型。它定義了處理事件的異步方法HandleAsync和處理異常的方法HandleException。接下來,我們有一個IEventBus接口,它定義了一些操作方法用于發(fā)布和訂閱事件。

其中,Publish<TEvent>PublishAsync<TEvent>方法用于發(fā)布事件,而OnSubscribe<TEvent>方法用于訂閱事件。然后,我們看到一個實現了本地事件總線的類LocalEventBusManager<TEvent>。它實現了ILocalEventBusManager<TEvent>接口,用于在單一管道內處理本地事件。它使用了一個Channel<TEvent>來存儲事件,并提供了發(fā)布事件的方法PublishPublishAsync。此外,它還提供了一個自動處理事件的方法AutoHandle。

總的來說Event Bus提供了一種方便的方式來實現組件之間的松耦合通信。

通過發(fā)布和訂閱事件,組件可以獨立地進行操作,而不需要直接依賴于彼此的實現細節(jié)。

這種機制可以提高代碼的可維護性和可擴展性。

Github倉庫地址:https://github.com/DonPangPang/soda-event-bus

實現一些基本約束

先實現一些約束,實現IEvent約束事件,實現IAsyncEvnetHandler<TEvent> where TEvent:IEvent來約束事件的處理程序。

public interface IEvent
{

}

public interface IAsyncEventHandler<in TEvent> where TEvent : IEvent
{
    Task HandleAsync(IEvent @event);

    void HandleException(IEvent @event, Exception ex);
}

接下來規(guī)定一下咱們的IEventBus,會有哪些操作方法。基本就是發(fā)布和訂閱。

public interface IEventBus
{
    void Publish<TEvent>(TEvent @event) where TEvent : IEvent;
    Task PublishAsync<TEvent>(TEvent @event) where TEvent : IEvent;

    void OnSubscribe<TEvent>() where TEvent : IEvent;
}

實現一個本地事件總線

本地事件處理

本地事件的處理我打算采用兩種方式實現,一種是LocalEventBusManager即本地事件管理,第二種是LocalEventBusPool池化本地事件。

LocalEvnetBusManager

LocalEventBusManager主要在單一管道內進行處理,集中進行消費。

public interface ILocalEventBusManager<in TEvent>where TEvent : IEvent
{
    void Publish(TEvent @event);
    Task PublishAsync(TEvent @event) ;
    
    void AutoHandle();
}

public class LocalEventBusManager<TEvent>(IServiceProvider serviceProvider):ILocalEventBusManager<TEvent>
    where TEvent: IEvent
{
    readonly IServiceProvider _servicesProvider = serviceProvider;

    private readonly Channel<TEvent> _eventChannel = Channel.CreateUnbounded<TEvent>();

    public void Publish(TEvent @event)
    {
        Debug.Assert(_eventChannel != null, nameof(_eventChannel) + " != null");
        _eventChannel.Writer.WriteAsync(@event);
    }

    private CancellationTokenSource Cts { get; } = new();

    public void Cancel()
    {
        Cts.Cancel();
    }
    
    public async Task PublishAsync(TEvent @event)
    {
        await _eventChannel.Writer.WriteAsync(@event);
    }

    public void AutoHandle()
    {
        // 確保只啟動一次
        if (!Cts.IsCancellationRequested) return;

        Task.Run(async () =>
        {
            while (!Cts.IsCancellationRequested)
            {
                var reader = await _eventChannel.Reader.ReadAsync();
                await HandleAsync(reader);
            }
        }, Cts.Token);
    }

    async Task HandleAsync(TEvent @event)
    {
        var handler = _servicesProvider.GetService<IAsyncEventHandler<TEvent>>();

        if (handler is null)
        {
            throw new NullReferenceException($"No handler for event {@event.GetType().Name}");
        }
        try
        {
            await handler.HandleAsync(@event);
        }
        catch (Exception ex)
        {
            handler.HandleException( @event, ex);
        }
    }
}

LocalEventBusPool

LocalEventBusPool即所有的Event都會有一個單獨的管道處理,單獨消費處理,并行能力更好一些。

public sealed class LocalEventBusPool(IServiceProvider serviceProvider)
{
    private readonly IServiceProvider _serviceProvider = serviceProvider;

    private class ChannelKey
    {
        public required string Key { get; init; }
        public int Subscribers { get; set; }

        public override bool Equals(object? obj)
        {
            if (obj is ChannelKey key)
            {
                return string.Equals(key.Key, Key, StringComparison.OrdinalIgnoreCase);
            }

            return false;
        }

        public override int GetHashCode()
        {
            return 0;
        }
    }

    private Channel<IEvent> Rent(string channel)
    {
        _channels.TryGetValue(new ChannelKey() { Key = channel }, out var value);

        if (value != null) return value;
        value = Channel.CreateUnbounded<IEvent>();
        _channels.TryAdd(new ChannelKey() { Key = channel }, value);
        return value;
    }

    private Channel<IEvent> Rent(ChannelKey channelKey)
    {
        _channels.TryGetValue(channelKey, out var value);
        if (value != null) return value;
        value = Channel.CreateUnbounded<IEvent>();
        _channels.TryAdd(channelKey, value);
        return value;
    }

    private readonly ConcurrentDictionary<ChannelKey, Channel<IEvent>> _channels = new();

    private CancellationTokenSource Cts { get; } = new();

    public void Cancel()
    {
        Cts.Cancel();
        _channels.Clear();
        Cts.TryReset();
    }

    public async Task PublishAsync<TEvent>(TEvent @event) where TEvent : IEvent
    {
        await Rent(typeof(TEvent).Name).Writer.WriteAsync(@event);
    }

    public void Publish<TEvent>(TEvent @event) where TEvent : IEvent
    {
        Rent(typeof(TEvent).Name).Writer.TryWrite(@event);
    }

    public void OnSubscribe<TEvent>() where TEvent : IEvent
    {
        var channelKey = _channels.FirstOrDefault(x => x.Key.Key == typeof(TEvent).Name).Key ??
                         new ChannelKey() { Key = typeof(TEvent).Name };
        channelKey.Subscribers++;

        Task.Run(async () =>
        {
            try
            {
                while (!Cts.IsCancellationRequested)
                {
                    var @event = await ReadAsync(channelKey);

                    var handler = _serviceProvider.GetService<IAsyncEventHandler<TEvent>>();
                    if (handler == null) throw new NullReferenceException($"No handler for Event {typeof(TEvent).Name}");
                    try
                    {
                        await handler.HandleAsync((TEvent)@event);
                    }
                    catch (Exception ex)
                    {
                        handler.HandleException((TEvent)@event, ex);
                    }
                }
            }
            catch (Exception e)
            {
                throw new InvalidOperationException("Error on onSubscribe handler", e);
            }
        }, Cts.Token);
    }

    private async Task<IEvent> ReadAsync(string channel)
    {
        return await Rent(channel).Reader.ReadAsync(Cts.Token);
    }

    private async Task<IEvent> ReadAsync(ChannelKey channel)
    {
        return await Rent(channel).Reader.ReadAsync(Cts.Token);
    }
}

LocalEventBus

實現LocalEventBus繼承自IEventBus即可,如果有需要擴展的方法自行添加,池化和管理器的情況單獨處理。

public interface ILocalEventBus: IEventBus
{

}
public class LocalEventBus(IServiceProvider serviceProvider, LocalEventBusOptions options) : ILocalEventBus
{
    private  LocalEventBusPool? EventBusPool => serviceProvider.GetService<LocalEventBusPool>();
    
    
    public void Publish<TEvent>(TEvent @event) where TEvent : IEvent
    {
        if (options.Pool)
        {
            Debug.Assert(EventBusPool != null, nameof(EventBusPool) + " != null");
            EventBusPool.Publish(@event);
        }
        else
        {
            var manager = serviceProvider.GetService<LocalEventBusManager<TEvent>>();
            if (manager is null) throw new NullReferenceException($"No manager for event {typeof(TEvent).Name}, please add singleton service it.");
            manager.Publish(@event);
        }
    }

    public async Task PublishAsync<TEvent>(TEvent @event) where TEvent : IEvent
    {
        if (options.Pool)
        {
            Debug.Assert(EventBusPool != null, nameof(EventBusPool) + " != null");
            await EventBusPool.PublishAsync(@event);
        }
        else
        {
            var manager = serviceProvider.GetService<LocalEventBusManager<TEvent>>();
            if (manager is null) throw new NullReferenceException($"No manager for event {typeof(TEvent).Name}, please add singleton service it.");
            await manager.PublishAsync(@event);
        }
    }

    public void OnSubscribe<TEvent>() where TEvent : IEvent
    {
        if (options.Pool)
        {
            Debug.Assert(EventBusPool != null, nameof(EventBusPool) + " != null");
            EventBusPool.OnSubscribe<TEvent>();
        }
        else
        {
            var manager = serviceProvider.GetService<LocalEventBusManager<TEvent>>();
            if (manager is null) throw new NullReferenceException($"No manager for event {typeof(TEvent).Name}, please add singleton service it.");
            manager.AutoHandle();
        }
    }
}

分布式事件總線

根據需要擴展即可,基本邏輯相同,但可能需要增加確認機制等。

最后

到此這篇關于詳解C#如何實現一個事件總線的文章就介紹到這了,更多相關C#實現事件總線內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • C# 鍵盤Enter鍵取代Tab鍵實現代碼

    C# 鍵盤Enter鍵取代Tab鍵實現代碼

    這篇文章主要介紹了C# 鍵盤Enter鍵取代Tab鍵實現代碼,有需要的朋友可以參考一下
    2013-11-11
  • C#操作XML通用方法匯總

    C#操作XML通用方法匯總

    這篇文章主要為大家詳細介紹了C#操作XML通用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • Unity?UGUI的TouchInputModule觸摸輸入模塊組件介紹使用示例

    Unity?UGUI的TouchInputModule觸摸輸入模塊組件介紹使用示例

    這篇文章主要為大家介紹了Unity?UGUI的TouchInputModule觸摸輸入模塊組件介紹使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • C#實現簡易計算器功能(2)(窗體應用)

    C#實現簡易計算器功能(2)(窗體應用)

    這篇文章主要為大家詳細介紹了C#實現簡易計算器功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • C#實現寫入與讀出文本文件的實例代碼

    C#實現寫入與讀出文本文件的實例代碼

    本篇文章是對使用C#實現寫入與讀出文本文件的方法進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • C#創(chuàng)建IIS虛擬目錄的方法

    C#創(chuàng)建IIS虛擬目錄的方法

    這篇文章主要介紹了C#創(chuàng)建IIS虛擬目錄的方法,實例分析了C#在IIS服務器上創(chuàng)建虛擬目錄的相關技巧,需要的朋友可以參考下
    2015-06-06
  • webBrowser代理設置c#代碼

    webBrowser代理設置c#代碼

    本文將介紹C# 為webBrowser設置代理實現代碼,需要了解的朋友可以參考下
    2012-11-11
  • Unity創(chuàng)建平鋪網格地圖的方法

    Unity創(chuàng)建平鋪網格地圖的方法

    這篇文章主要為大家詳細介紹了Unity創(chuàng)建平鋪網格地圖的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • c#基礎學習之封裝

    c#基礎學習之封裝

    說到封裝,其實是比較基礎類的問題,它為程序設計提供了系統(tǒng)與系統(tǒng),模塊與模塊,類與類之間交互的實現手段
    2013-09-09
  • C#屬性get和set使用示例詳解

    C#屬性get和set使用示例詳解

    屬性是C#中的一種特殊成員,它允許外部以受控方式訪問類的狀態(tài),屬性通過get和set方法實現對類私有字段的讀取和修改,本文給大家介紹C#屬性get和set使用,感興趣的朋友一起看看吧
    2024-09-09

最新評論