Winforms IoC Container - 如何使用演示者工厂处理具体类型

时间:2016-05-03 04:39:11

标签: c# winforms generics simple-injector

背景

我使用带有MVP模式的Winforms来创建应用程序。我使用SimpleInjector作为我的IoC容器。我的演讲者继承自:

public interface IPresenter<TView>
{
    TView View { get; set; }
}

internal class HomePresenter : IPresenter<IHomeView>
{
    public IHomeView View { get; set; }

    ...
}

为了创建我的演示者,我决定使用以下方法的演示者工厂:

public static IPresenter<TView> CreateForView<TView>(TView view)
    {
        var presenter = _container.GetInstance<IPresenter<TView>>();
        presenter.View = view;
        return presenter;
    }

然后在每个视图中,视图通过调用演示者工厂创建自己的演示者:

_homeMainPresenter = (HomePresenter) presenterFactory.CreateForView<IHomeView>(this);
_homeMainPresenter.View = this;

在我的Program.cs文件中,我有:

    static void Main()
    {
        System.Windows.Forms.Application.EnableVisualStyles();
        System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);

        Bootstrap();

        System.Windows.Forms.Application.Run((HomeView)container.GetInstance<IHomeView>());
    }

    private static void Bootstrap()
    {
        // Create the container
        container = new Container();

        // Register types
        container.Register<IHomeView, HomeView>(Lifestyle.Singleton);
        container.Register<IPresenter<IHomeView>, HomePresenter>();
        ...

        // Verify the container
        container.Verify();
    }

问题

当从HomeView视图调用演示者工厂时,输入工厂的类型是HomeView类型,而不是IHomeView。因此,应用程序抛出异常,因为容器没有HomeView注册(仅IHomeView)。我的演示者都有他们存储的视图参考的接口,因为我觉得这对测试更好。我该如何避免这种情况?

1 个答案:

答案 0 :(得分:2)

对表单实现接口到实现的绑定没有用,因为它们是表示技术的根类型。大多数演示技术无论如何都无法处理自定义抽象,这就是您将IHomeView强制转换回HomeView以允许将其传递给Application.Run方法的原因

您可以执行以下操作,而不是从视图中解析演示者:

public interface IHomeView { }

public interface IPresenter<TView> {
    TView View { get; set; }
}

public class HomeView : Form, IHomeView
{
    private readonly IPresenter<IHomeView> presenter;

    public HomeView(IPresenter<IHomeView> presenter) {
        this.presenter = presenter;
        InitializeComponent();
    }
}

这里Form注入一个IPresenter<IHomeView>并存储传入的依赖项。不再需要工厂,可以从代码中删除。

在你的程序主页中:

static void Main()
{
    System.Windows.Forms.Application.EnableVisualStyles();
    System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);

    Bootstrap();

    System.Windows.Forms.Application.Run(GetForm<HomeView, IHomeView>(container));
}

private static void Bootstrap()
{
    // Create the container
    container = new Container();

    // Register types
    // NOTE: We register HomeView as concrete type; not by its interface.
    container.Register<HomeView>(Lifestyle.Singleton);

    // Here we batch-register all presenters with one line of code and
    // since the forms depend on them, they need to be singletons as well.
    container.Register(typeof(IPresenter<>),  AppDomain.CurrentDomain.GetAssemblies(), 
        Lifestyle.Singleton);
    ...

    // Verify the container
    container.Verify();
}

private static TForm GetForm<TForm, TView>() where TForm : Form, TView
{
    var form = container.GetInstance<TForm>();
    container.GetInstance<IPresenter<TView>>().View = form;
    return form;
}

现在,工厂类已替换为组合根的GetForm方法;表单无法访问它。通用类型允许我们解析适当的演示者,同时保持代码类型安全。