如何传播对象参考更改?

时间:2018-10-18 23:04:24

标签: c# dependency-injection

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

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

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

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

(我可以修改MainClass的构造函数以使用自定义存储库,但想知道在MainClass创建之后如何设置存储库)

MainClass

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);
}

1 个答案:

答案 0 :(得分:0)

理想情况下,您希望您的DI容器可以进行 run 的操作:

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<>以解决新问题。每次调用实例:

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();
  }
}