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

WinForms中基于MVP模式与Lamar依赖注入的Presenter创建时机与方法

C# WinForms MVP+Lamar DI 实践问题解析

背景概述

我在C# Windows Forms应用中采用Model-View-Presenter(MVP)观察式Presenter风格,结合Lamar依赖注入(DI)框架开发,解决方案分为Infrastructure、Domain、Presentation三个项目:

  • Presentation项目的窗体和用户控件按MVP模式拆分,视图采用无参构造函数以支持设计时拖拽控件
  • 所有业务逻辑由Presenter处理,Presenter构造函数需传入Domain项目接口及视图接口,完全解除视图对Presenter的依赖

Program.cs 容器注册代码

var container = new Container(x =>
{
    x.AddSingleton<IInterfaceFromDomainProject, ClassFromDomainProject>();
    x.AddSingleton<IMainView, MainView>();
    x.AddSingleton<IMainPresenter, MainPresenter>();
    x.AddSingleton<ISubView, SubView>();
    x.AddSingleton<ISubPresenter, SubPresenter>();
});

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mainView = new MainView();
Application.Run(mainView);

主视图代码示例

public interface IMainView
{
    event EventHandler MyCustomEvent;
    void ShowMessage(); 
}

public partial class MainView : Form, IMainView
{
    public event EventHandler MyCustomEvent;

    public MainView()
    {
        InitializeComponent();
    }

    private void button_Click(object sender, EventArgs e)
    {
        MyCustomEvent.Invoke(sender, EventArgs.Empty);
    }

    public void ShowMessage()
    {
        MessageBox.Show("Hello!");
    }
}

主Presenter代码示例

public interface IMainPresenter
{
    void ShowMessageHandler(object sender, EventArgs e);
    void ShowData();
}

public class MainPresenter: IMainPresenter
{
    private readonly IMainView _view;
    private readonly IInterfaceFromDomainProject _foo;

    public MainPresenter(IMainView view, IInterfaceFromDomainProject foo)
    {
        _view = view;
        _foo = foo;

        _view.MyCustomEvent += ShowMessageHandler;
    }

    public void ShowMessageHandler(object sender, EventArgs e)
    {
        _view.ShowMessage();
    }

    public void ShowData()
    {
        // 调用_foo获取数据并展示到视图
    }
}

核心问题

  1. 基于当前MVP与DI实现,应如何及何时创建Presenter对象?
  2. 是否需要在Program.cs中为所有Presenter手动获取依赖实例创建?
  3. 对DI或MVP模式是否存在理解偏差?
  4. 补充:通过容器获取依赖创建Presenter时,MyCustomEvent.Invoke(sender, EventArgs.Empty)处抛出NullReferenceException;仅直接传入实例化视图能正常运行;不知如何在视图构造函数中创建Presenter并传入Domain接口依赖。

解决方案

1. Presenter的正确创建时机与方式

不需要手动创建Presenter,应完全交由Lamar容器管理:

  • 不要直接new MainView(),而是从容器解析Presenter或视图,容器会自动完成依赖注入与事件绑定
  • 修改Program.cs的启动逻辑,两种可选实现:

方式1:解析Presenter关联视图

var container = new Container(x =>
{
    x.AddSingleton<IInterfaceFromDomainProject, ClassFromDomainProject>();
    x.AddSingleton<IMainView, MainView>();
    x.AddSingleton<IMainPresenter, MainPresenter>();
    x.AddSingleton<ISubView, SubView>();
    x.AddSingleton<ISubPresenter, SubPresenter>();
});

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

// 从容器解析Presenter,容器自动注入View与Domain依赖并完成事件绑定
var mainPresenter = container.GetInstance<IMainPresenter>();
// 获取Presenter关联的View(可在IMainPresenter中添加View属性优化此逻辑)
var mainView = (IMainView)typeof(MainPresenter).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(mainPresenter);
Application.Run((Form)mainView);

方式2:用Lamar初始化器自动绑定

var container = new Container(x =>
{
    x.AddSingleton<IInterfaceFromDomainProject, ClassFromDomainProject>();
    x.AddSingleton<IMainView, MainView>();
    x.AddSingleton<IMainPresenter, MainPresenter>();
    x.AddSingleton<ISubView, SubView>();
    x.AddSingleton<ISubPresenter, SubPresenter>();

    // 解析View时自动初始化对应的Presenter,完成事件绑定
    x.For<IMainView>().OnCreation((context, view) =>
    {
        context.GetInstance<IMainPresenter>();
    });
});

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

// 直接从容器获取View,初始化器自动创建并绑定Presenter
var mainView = container.GetInstance<IMainView>();
Application.Run((Form)mainView);

2. 空引用异常的原因与解决

  • 异常原因:手动new MainView()时,对应的Presenter未被实例化,MyCustomEvent无订阅者,触发时为null
  • 解决:通过容器解析View或Presenter,确保Presenter在View使用前完成实例化与事件订阅

3. MVP与DI的理解修正

  • 你的观察式MVP设计是正确的:View不依赖Presenter,仅暴露事件与展示接口,Presenter处理业务逻辑并订阅View事件
  • DI的核心是容器管理依赖生命周期与注入,无需手动创建Presenter,让容器自动处理所有依赖的创建与绑定
  • 不要在View构造函数中创建Presenter,这会破坏View与Presenter的解耦设计,违背MVP的核心原则

4. 子视图/子Presenter的处理

对于SubView和SubPresenter,遵循相同逻辑:

  • 确保容器已注册对应的接口与实现
  • 在主Presenter中需要使用子视图时,从容器解析ISubPresenter或ISubView,容器会自动注入依赖并完成事件绑定

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 16:00:50