最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#實(shí)現(xiàn)事件總線的方法示例

 更新時(shí)間:2024年02月19日 10:16:20   作者:Archy_Wang_1  
事件總線是一種用于在應(yīng)用程序內(nèi)部或跨應(yīng)用程序組件之間進(jìn)行事件通信的機(jī)制,本文主要介紹了C#實(shí)現(xiàn)事件總線的方法示例,具有一定的參考價(jià)值,感興趣的可以了解一下

EventBus(事件總線)是一種用于在應(yīng)用程序內(nèi)部或跨應(yīng)用程序組件之間進(jìn)行事件通信的機(jī)制。

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

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

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

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

總的來(lái)說(shuō)Event Bus提供了一種方便的方式來(lái)實(shí)現(xiàn)組件之間的松耦合通信。

通過(guò)發(fā)布和訂閱事件,組件可以獨(dú)立地進(jìn)行操作,而不需要直接依賴于彼此的實(shí)現(xiàn)細(xì)節(jié)。

這種機(jī)制可以提高代碼的可維護(hù)性和可擴(kuò)展性。

Github倉(cāng)庫(kù)地址:https://github.com/DonPangPang/soda-event-bus

實(shí)現(xiàn)一些基本約束

先實(shí)現(xiàn)一些約束,實(shí)現(xiàn)IEvent約束事件,實(shí)現(xiàn)IAsyncEvnetHandler<TEvent> where TEvent:IEvent來(lái)約束事件的處理程序。

public interface IEvent
{

}

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

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

接下來(lái)規(guī)定一下咱們的IEventBus,會(huì)有哪些操作方法。基本就是發(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;
}

實(shí)現(xiàn)一個(gè)本地事件總線

本地事件處理

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

LocalEvnetBusManager

LocalEventBusManager主要在單一管道內(nèi)進(jìn)行處理,集中進(jìn)行消費(fèi)。

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()
    {
        // 確保只啟動(dòng)一次
        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都會(huì)有一個(gè)單獨(dú)的管道處理,單獨(dú)消費(fèi)處理,并行能力更好一些。

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

實(shí)現(xiàn)LocalEventBus繼承自IEventBus即可,如果有需要擴(kuò)展的方法自行添加,池化和管理器的情況單獨(dú)處理。

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();
        }
    }
}

分布式事件總線

根據(jù)需要擴(kuò)展即可,基本邏輯相同,但可能需要增加確認(rèn)機(jī)制等。

到此這篇關(guān)于C#實(shí)現(xiàn)事件總線的方法示例的文章就介紹到這了,更多相關(guān)C# 事件總線內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • WPF實(shí)現(xiàn)自定義Panel面板的示例詳解

    WPF實(shí)現(xiàn)自定義Panel面板的示例詳解

    WPF中的Panel(面板),是繼承自FrameworkElement的抽象類,表示一個(gè)可以用來(lái)排列子元素的面板,本文主要來(lái)和大家聊聊WPF如何實(shí)現(xiàn)自定義Panel,感興趣的可以了解下
    2023-09-09
  • C#窗體實(shí)現(xiàn)點(diǎn)餐系統(tǒng)

    C#窗體實(shí)現(xiàn)點(diǎn)餐系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C#窗體實(shí)現(xiàn)點(diǎn)餐系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • C#中多種高效定時(shí)器方法的使用詳解

    C#中多種高效定時(shí)器方法的使用詳解

    本文主要介紹了C#中多種高效定時(shí)器方法的使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • C#鎖住文件的操作步驟

    C#鎖住文件的操作步驟

    在本篇文章里小編給大家整理的是關(guān)于C#鎖住文件的操作步驟以及相關(guān)知識(shí)點(diǎn),需要的朋友們參考下。
    2019-08-08
  • HTML文本框的值改變后觸發(fā)后臺(tái)代碼的方法

    HTML文本框的值改變后觸發(fā)后臺(tái)代碼的方法

    asp.net用日期插件,當(dāng)選中一個(gè)日期時(shí)觸發(fā)一個(gè)事件,以查詢當(dāng)前日期的數(shù)據(jù)。這是要跟數(shù)據(jù)庫(kù)交互的。先貼出控件代碼:
    2013-04-04
  • 基于C#實(shí)現(xiàn)的文件分割與合并工具

    基于C#實(shí)現(xiàn)的文件分割與合并工具

    文件分割與合并是數(shù)據(jù)管理中的基礎(chǔ)操作,常用于大文件傳輸(如郵件附件限制)、分布式存儲(chǔ)(拆分后存儲(chǔ)至多個(gè)介質(zhì))、數(shù)據(jù)備份(分卷壓縮)等場(chǎng)景,本工具基于 C# 語(yǔ)言開(kāi)發(fā)實(shí)現(xiàn)的文件分割與合并工具,需要的朋友可以參考下
    2026-04-04
  • C#微信開(kāi)發(fā)之獲取接口調(diào)用憑據(jù)

    C#微信開(kāi)發(fā)之獲取接口調(diào)用憑據(jù)

    本文主要介紹了C#微信開(kāi)發(fā)之接收 / 返回文本消息原理與實(shí)現(xiàn)方法,具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-02-02
  • 用C#實(shí)現(xiàn)啟動(dòng)另一程序的方法實(shí)例

    用C#實(shí)現(xiàn)啟動(dòng)另一程序的方法實(shí)例

    一段實(shí)例代碼,程序的目的是使用C#實(shí)現(xiàn)啟動(dòng)另一程序的方法。技術(shù)總監(jiān)給出了我們這樣一個(gè)有效的啟動(dòng)程序的有效方法,現(xiàn)在和大家分享下
    2013-07-07
  • C# 調(diào)用exe傳參,并獲取打印值的實(shí)例

    C# 調(diào)用exe傳參,并獲取打印值的實(shí)例

    這篇文章主要介紹了C# 調(diào)用exe傳參,并獲取打印值的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • C#使用HttpClient進(jìn)行Post請(qǐng)求出現(xiàn)超時(shí)問(wèn)題的解決及優(yōu)化

    C#使用HttpClient進(jìn)行Post請(qǐng)求出現(xiàn)超時(shí)問(wèn)題的解決及優(yōu)化

    最近我的控制臺(tái)程序發(fā)現(xiàn)有時(shí)候總是出現(xiàn)請(qǐng)求超時(shí)等問(wèn)題,通常好幾分鐘最多只有3-4個(gè)請(qǐng)求,在使用apipost發(fā)現(xiàn)并發(fā)10個(gè)5分鐘也沒(méi)有問(wèn)題,那么問(wèn)題就出在我的請(qǐng)求端了,所以本文給大家介紹了C# 使用HttpClient進(jìn)行Post請(qǐng)求總是出現(xiàn)超時(shí)問(wèn)題的優(yōu)化,需要的朋友可以參考下
    2025-01-01

最新評(píng)論

彭泽县| 阿拉善右旗| 军事| 锡林郭勒盟| 德清县| 五华县| 合水县| 新密市| 盐源县| 甘肃省| 离岛区| 丹寨县| 水城县| 九江县| 鹿邑县| 浙江省| 保德县| 方山县| 绥德县| 潮州市| 乐清市| 巴林右旗| 运城市| 彭阳县| 三亚市| 武城县| 兴安盟| 武冈市| 台安县| 江陵县| 漾濞| 应城市| 罗城| 巨野县| 九寨沟县| 昌黎县| 团风县| 广元市| 和静县| 上高县| 来宾市|