在 C# 中的 WinForms 上使用 Microsoft 扩展依赖注入

Ami*_*ati 10 c# dependency-injection winforms

我在 DI 方面的知识非常有限。我正在一个解决方案中开发一个 WinForm 项目,该解决方案中的所有其他地方都使用了Microsoft 扩展依赖注入

我需要将一些依赖项传递给 MainForm 的构造函数:

public partial class MainForm : Form
{
   public MainForm(ISomeThing someThing)
   {
   }
}
Run Code Online (Sandbox Code Playgroud)

在 Main 方法中,将 MainForm 的一个实例传递给 Run 方法:

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new MainForm());
}
Run Code Online (Sandbox Code Playgroud)

我尝试使用 DI 通过服务提供者实例化 Mainform 的一个实例:

private static IServiceProvider ServiceProvider { get; set; }
Run Code Online (Sandbox Code Playgroud)

然后为它分配一个对象,如下所示:

static void ConfigureServices()
{
   var services = new ServiceCollection();
   services.AddTransient<ISomeThing, SomeThing>();
   ServiceProvider = services.BuildServiceProvider();
}
Run Code Online (Sandbox Code Playgroud)

然后调用ConfigureServices()Main(),如下所示:

static void Main()
{
   Application.EnableVisualStyles();
   Application.SetCompatibleTextRenderingDefault(false);
   ConfigureServices();
   Application.Run(ServiceProvider.GetService(MainForm));
}
Run Code Online (Sandbox Code Playgroud)

但是,我收到一个编译错误:“ MainForm 是一种类型,在给定的上下文中无效

我发现以下链接使用SimpleInjectorUnity使用类似方法, 但我不知道如何将其与这种 DI 一起使用?或者我必须使用其他DI?

谢谢

SLa*_*aks 9

您正在尝试获取与System.Type该类对应的实例。

这就是typeof()关键字的作用:

GetService(typeof(MainForm))
Run Code Online (Sandbox Code Playgroud)

请注意,您还需要将结果转换为Form.


Goo*_*ies 6

我会修改它如下..

您不需要使用参数将任何内容传递给 MainForm 的构造函数。

在 Program 类中,Something 的一个实例被注册为 ISomething 的实现者,但 Application.Run() 保持不变,

using Microsoft.Extensions.DependencyInjection;  // provides DI
using SomeThing; // implements ISomeThing interface and SomeThing class 

/*Program.cs */ 
public static class Program
{
  //..
  public static IServiceProvider ServiceProvider { get; set; }

  static void ConfigureServices()
  {
    var services = new ServiceCollection();
    services.AddTransient<ISomeThing, SomeThing>();
    ServiceProvider = services.BuildServiceProvider();
  }

  [STAThread]
  static void Main()
  {  
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    ConfigureServices();
    Application.Run(new MainForm());
  }
}
Run Code Online (Sandbox Code Playgroud)

现在在 MainForm 中,构造函数将查询注册的依赖项并将其保存为私有属性,例如

    public class MainForm: Form
    {

      // ..

       private readonly IMySomeThing _mySomething;

       public MainForm()
       {
        _mySomething =   (IMySomeThing)Program.ServiceProvider.GetService(typeof(IMySomeThing));
       }     

    }
Run Code Online (Sandbox Code Playgroud)

在 MainForm ctor 完成后,可以从 MainForm 的任何地方访问私有字段 _mySomething(或其方法可以调用)。哪个类实现 IMySomething 由 ServiceProvider 的注册控制。这允许通过替换某些拥有的类来更改类的行为。

我使用 DI 来简化某些单元测试。例如,在单元测试中,实现一些复杂子任务的依赖项可以用简单的真/假返回替代来代替。这种测试方法通常被称为“模拟”。它允许对中级和高级程序进行单元测试。

  • 这完全隐藏了类的依赖关系,并且正在朝着服务定位器模式发展,但这并不总是好的。假设其他人想要将此 MainForm 用于另一个项目,他不知道他应该提供 IMySomething,那么一切都会对他造成破坏。这是我的观点,类的所有依赖项都应该通过构造函数提供。 (3认同)

com*_*eye 5

我在代码中添加了一个辅助函数,以使其更具可读性:

public static T? GetService<T>() where T : class
{
   return (T?)ServiceProvider.GetService(typeof(T));
}

Run Code Online (Sandbox Code Playgroud)


//was:
// thing = (iThing)Program.ServiceProvider.GetService(typeof(iThing));

//now:
thing = Program.GetService<iThing>();


Run Code Online (Sandbox Code Playgroud)