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

C# WPF依赖注入场景下类间通信避免循环引用的最优方案

符合OOP规则的通用解决方案:基于事件聚合器的解耦通信

该方案完全避开循环依赖问题,同时适配动态切换顶部视图的需求,严格遵循依赖倒置、单一职责、开闭原则等OOP核心规则。

核心逻辑

两个ViewModel均不直接依赖对方,仅依赖全局唯一的事件聚合器中介完成通信:

  • 发送方仅需向事件聚合器发布指定类型的事件
  • 接收方仅需向事件聚合器订阅自己关心的事件类型
  • 双方完全感知不到对方的存在,耦合度降到最低

具体实现步骤

1. 定义事件聚合器抽象与实现

首先定义通用的事件发布订阅接口:

public interface IEventAggregator
{
    void Subscribe<TEvent>(Action<TEvent> handler);
    void Publish<TEvent>(TEvent eventData);
    void Unsubscribe<TEvent>(Action<TEvent> handler);
}

简易实现(可直接投入生产使用):

public class EventAggregator : IEventAggregator
{
    private readonly ConcurrentDictionary<Type, List<object>> _handlers = new();

    public void Subscribe<TEvent>(Action<TEvent> handler)
    {
        var eventType = typeof(TEvent);
        if (!_handlers.ContainsKey(eventType))
            _handlers[eventType] = new List<object>();
        _handlers[eventType].Add(handler);
    }

    public void Publish<TEvent>(TEvent eventData)
    {
        var eventType = typeof(TEvent);
        if (_handlers.TryGetValue(eventType, out var handlers))
        {
            foreach (var handler in handlers.Cast<Action<TEvent>>())
                handler(eventData);
        }
    }

    public void Unsubscribe<TEvent>(Action<TEvent> handler)
    {
        var eventType = typeof(TEvent);
        if (_handlers.TryGetValue(eventType, out var handlers))
            handlers.Remove(handler);
    }
}

2. DI容器注册

将事件聚合器注册为全局单例:

services.AddSingleton<IEventAggregator, EventAggregator>();

3. ViewModel通信实现

仅需在两个ViewModel的构造函数中注入IEventAggregator,无需依赖对方。

示例1:TopViewModel触发命令通知ContentViewModel

首先定义事件类(普通POCO即可):

public class SearchTriggeredEvent
{
    public string Keyword { get; set; }
}

TopViewModel发布事件:

public class TopViewModel
{
    private readonly IEventAggregator _eventAggregator;
    public ICommand SearchCommand { get; }

    public TopViewModel(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
        SearchCommand = new RelayCommand<string>(keyword => 
        {
            _eventAggregator.Publish(new SearchTriggeredEvent { Keyword = keyword });
        });
    }
}

ContentViewModel订阅事件:

public class ContentViewModel
{
    private readonly IEventAggregator _eventAggregator;

    public ContentViewModel(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
        // 订阅搜索事件
        _eventAggregator.Subscribe<SearchTriggeredEvent>(OnSearchTriggered);
    }

    private void OnSearchTriggered(SearchTriggeredEvent e)
    {
        // 执行搜索逻辑
        Console.WriteLine($"执行搜索:{e.Keyword}");
    }
}

示例2:ContentViewModel通知TopViewModel更新状态

仅需新增对应事件类,ContentViewModel发布、TopViewModel订阅即可,无需修改现有通信逻辑。

4. 适配动态切换顶部视图的需求

切换顶部视图时,只需在旧TopViewModel销毁前调用Unsubscribe方法取消所有事件订阅,新TopViewModel初始化时注册对应订阅即可,ContentViewModel无需做任何调整。

方案优势

  • 完全消除循环依赖问题,两个ViewModel的初始化互不影响
  • 符合OOP设计原则:双方仅依赖抽象的事件聚合器,不依赖具体实现;新增通信需求仅需新增事件类,无需修改现有VM代码
  • 适配动态视图切换场景,扩展成本极低
  • 可复用到其他跨ViewModel通信场景,比如主菜单ViewModel和内容ViewModel的通信

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 21:15:00