本文用一个非常简单的示例来演示一下如何使用Unity和StructureMap在C#中实现Dependency Injection。
我们来做一个非常简单的程序,这个程序会把用户输入的字符串做个逆序,然后输出,同时要求记录一下每次用户的输入和结果,我们支持两种Logger,一种是命令行的,一种是对话框的,用户可以选择使用哪种Logger。
界面如下:
这个程序使用MVP来实现的,我们有4个接口如下,分别对应V,P,M和Logger:
public interface IView { void DisplayResult(string result); } public interface IPresenter { void HandleReverse(string text, IView view); void SetLogger(ILogger logger); } internal interface IModel { string Reverse(string text); } public interface ILogger { void LogMessage(string message); }
1. 手工实现依赖注入和singleton。
在App.xaml.cs中我们通过下面的方法来打开UI。可以看到我们用了一堆的new关联起来,创建了一个view。
protected override void OnStartup(StartupEventArgs e) { createViewByHand().Show(); } private static Window createViewByHand() { return new MainWindow(new Presenter(LoggerFactory.CreateLogger(), ModelFactory.CreateModel())); }
这里ModelFactory和LoggerFactory是为了保证我们的应用中只有一个Model和Logger的实例。(其实主要是logger,因为model在我们的应用里就一个,但是用户可以选择不同的logger,我们不希望每回用户选择之后都生成一个新的logger)
用户选择下拉框的代码如下:
private void loggerTypeChanged(object sender, SelectionChangedEventArgs e) { m_Controller.SetLogger(getLoggerByHand()); } private ILogger getLoggerByHand() { return LoggerFactory.CreateLogger(loggerType.SelectedItem.ToString()); }
2. 使用StructureMap实现依赖注入和singleton。
代码如下,我们创建一个Container,配置对应于每个接口,我们使用哪个实例。注意我们在ILogger和IModel中使用了Singleton方法,同时我们配置了2个ILogger的实例,分别设置了一个名字,在用户改变下拉框时,我们从Container中取出相应的实例。
private static Window createViewByStructureMap() { StructureMapContainer = new StructureMap.Container(x => { x.For<IPresenter>().Use<Presenter>(); x.For<IView>().Use<MainWindow>(); x.For<IModel>().Singleton().Use<ReverseModel>(); x.AddType(typeof(ILogger), typeof(MessageBoxLogger), LoggerFactory.MessageboxLoggerName); x.AddType(typeof(ILogger), typeof(ConsoleLogger), LoggerFactory.ConsoleLoggerName); x.For<ILogger>().Singleton().UseSpecial(y => y.TheInstanceNamed(LoggerFactory.ConsoleLoggerName)); }); StructureMapContainer.AssertConfigurationIsValid(); return StructureMapContainer.GetInstance<MainWindow>(); } private ILogger getLoggerByStructureMap() { return App.StructureMapContainer.GetInstance<ILogger>(loggerType.SelectedItem.ToString()); }
3. 使用Unity实现依赖注入和singleton。
代码非常类似,只是用ContainerControlledLifetimeManager来实现singleton。
private Window createViewByUnity() { UnityContainer = new UnityContainer(); UnityContainer.RegisterType<IPresenter, Presenter>(); UnityContainer.RegisterType<IView, MainWindow>(); UnityContainer.RegisterType<IModel, ReverseModel>(new ContainerControlledLifetimeManager()); UnityContainer.RegisterType<ILogger, ConsoleLogger>(LoggerFactory.ConsoleLoggerName, new ContainerControlledLifetimeManager()); UnityContainer.RegisterType<ILogger, MessageBoxLogger>(LoggerFactory.MessageboxLoggerName, new ContainerControlledLifetimeManager()); return UnityContainer.RegisterInstance(typeof(ILogger), UnityContainer.Resolve<ILogger>(LoggerFactory.ConsoleLoggerName)).Resolve<MainWindow>(); } private ILogger getLoggerByUnity() { return App.UnityContainer.Resolve<ILogger>(loggerType.SelectedItem.ToString()); }
另外StructureMap和Unity都支持Attribute来制定应该往哪个属性上Inject。StructureMap是[SetterProperty],Unity是 [Dependency]。
具体的实现就不详细列出来,可以在github上找到源码。