【问题标题】:How to propagate object reference change?如何传播对象引用更改?
【发布时间】:2018-10-18 23:04:23
【问题描述】:

我正在构建一个 Windows 窗体应用程序,它使用静态 MessageBox.Show() 方法不时显示一个 MessageBox。

我将静态调用包装在一个 Repository 对象中,如这个问题所示:How to use Dependency Injection with Static Methods?

在 MainClass 中,这个 Repository 对象默认使用原来的静态调用,但是 MainClass 还提供了一个方法 SetMessageBoxRepoDependency() 来设置这个 Repository 对象。这为依赖注入打开了选项。这样做的一个用途是使用模拟工具来引用 Repository 接口,并教导模拟的 Show 方法在测试期间不要创建真正的 MessageBox。

但是,MainClass 还创建了许多使用此 Repository 实例的其他对象。如何修改我的代码,以便在调用 MainClass 的 SetMessageBoxRepoDependency() 时,其他对象也将使用新的存储库?

(我可以修改 MainClass 的构造函数以接收自定义 Repository,但想知道在 MainClass 创建后如何设置 Repository)

主类

public class MainClass
{
    private IMessageBoxRepository messageBoxRepo;

    public MainClass()
    {
        messageBoxRepo = new MessageBoxRepository();

        // Just classes that reference the same Repository
        var classA = new ClassA(messageBoxRepo);
        var classB = new ClassB(messageBoxRepo);
        var classC = new ClassC(messageBoxRepo);
    }

    public void SetMessageBoxRepoDependency(IMessageBoxRepository messageBoxRepo)
    {
        this.messageBoxRepo = messageBoxRepo;
    }
}

MessageBoxRepository

public class MessageBoxRepository : IMessageBoxRepository
{
    public DialogResult Show(string text)
    {
        return MessageBox.Show(text); // The original static call.
    }
}

IMessageBoxRepository

public interface IMessageBoxRepository
{
    DialogResult Show(string text);
}

【问题讨论】:

  • 这根本不像“依赖注入”。那里有所有的“新”关键字,根本没有依赖注入,你需要一个容器类来管理你的对象及其生命周期,并用 container.Resolve 替换你的所有新 ClassXXX() 如果你 MainClass 是你的复合根。
  • @LeY 我不认为这个类应该调用container.Resolve<>(),它应该像其他所有类一样通过构造函数传递。
  • 这个messageBoxRepo = new MessageBoxRepository(); 不是DI...
  • @LeY,如果MainClass 是应用程序的入口点,那么使用new 构建应用程序的对象图是正确的地方。请注意,依赖注入不需要框架或容器。 Container是DI框架的实现细节。

标签: c# dependency-injection


【解决方案1】:

理想情况下,您希望您的 DI 容器能够运行一些东西:

namespace WindowsFormsApp1
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            var container = GetContainer();

            // most likely the only resolve you need.
            var form = container.Resolve<Form1>();

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(form);
        }

        private static IContainer GetContainer()
        {
           // Register Dependencies
           // Build the Container
           // return Container;
        }
    }
}

假设您使用的是 Autofac(我不了解其他框架,但它们应该能够做到这一点),任何形式都应该能够将其构造函数签名更改为 Func&lt;&gt; 以解析新实例调用:

public class Form1
{
  private Func<IMessageBoxRepository> _mbr;

  public Form1(Func<IMessageBoxRepository> mbr)
  {
    _mbr = mbr;
  }

  public void OnError(string msg)
  {
    var mb = _mbr();
    mb.Show(msg);
  }
}

这意味着您不必到处暴露您的容器(您不应该这样做,尤其是在您计划进行单元测试时)。

然后单元测试变得非常容易(示例假设为 NSubstitute,但大家都在做类似的事情):

public class Form1Test
{
  public void OnError_WithAny_CallsShow()
  {
    // Arrange
    var mbr = Substitute.For<IMessageBoxRepository>();
    var mbrFunc = Substitute.For<Func<IMessageBoxRepository>>();
    mbrFunc().Returns(mbr);
    var form1 = new Form1(mbrFunc);

    // Act
    form1.OnError(null);

    // Assert
    mbr.Received().Show();
  }
}

【讨论】:

    猜你喜欢
    • 2011-09-08
    • 1970-01-01
    • 2013-06-26
    • 1970-01-01
    • 2015-12-18
    • 2012-04-25
    • 2020-08-11
    • 2019-10-18
    • 1970-01-01
    相关资源
    最近更新 更多