我最近一直在涉足 IOC 容器(在我的例子中是 LightInject)。
我一直在阅读,您应该只需要在启动时使用容器一次,而无需在其他地方使用。这是我很难理解的。如果我只能在引导/启动方法中引用容器,那么如果类依赖于用户输入,如何在项目中的其他地方或运行时解决我需要的内容。
因此,在我的传统 Windows 窗体应用程序中,在表单加载时,我将按照以下代码引导 Lightinject。这只是一个任意的例子,它更像是我需要了解的前提。
我可能在这里完全错过了一些东西,或者只是没有得到它。但是我应该如何解决依赖关系,如果我不能使用/不应该引用或使用 Container.GetInstance/Resolve/{Choose IOC Syntax Here},并且只能在组合根中。
例如,假设我的表单上有两个按钮和一个 TextBox。第一个按钮给我一个 ILoader(下面的代码),第二个按钮加载一个文件查看器(ILoader,下面的代码),其文件名是输入到 winform 上的文本框中的文件名。
如果没有 IOC 容器,我会执行以下操作(假设将其放入 click 事件中)
按钮 1 单击事件:
ISplitText MyStringFunc = new WhateverImplementsIt();
按钮 2(根据文本框输入获取文件阅读器)
ILoader MyLoader = new FileReaderImplementation(TextBox1.Text);
使用 LightInject,我肯定不得不执行以下操作:
Button1 点击:
ISplitText Splitter = Container.GetInstance<ISplitText>();
按钮 2 单击
var LoaderFunc = Container.GetInstance<Func<string, ILoader>>();
ILoader l2 = LoaderFunc(TextBox1.Text);
我不正确吗?在一个大型项目中,我会有 Container.GetInstance,到处都是,在主表单文件和其他地方,所以我怎么能只在一个地方引用容器,以引导程序的形式,我错过了一个魔法一块拼图?
在我见过的所有示例应用程序中,这一切都在一个简单的控制台应用程序中完成,在 Main 函数中。所有这些应用程序都遵循以下格式:
Container = new Container();
Container.Register<IFoo,Foo>();
Container.Register<IBar,Bar();
var Resolved = Container.GetInstance<IFoo>();
嗯,我明白这一切,而且非常简单。一旦您开始为应用程序本身添加一些复杂性,我就迷失了如何在不使容器本身公开、静态或以某种方式、形状或形式访问然后调用 Container.GetInstance 的情况下获取实例在一百万个地方(显然,这是一个很大的禁忌)。请帮忙!干杯,
楚德
PS - 我不关心“抽象容器”本身。所以宁愿只专注于增加我对上述内容的理解。
public class BootStrapIOC
{
public ServiceContainer Container;
public BootStrapIOC(ServiceContainer container)
{
Container = container;
}
public void Start()
{
Container.Register<ISplitText, StringUtil>();
Container.Register<string, ILoader>((factory, value) => new FileViewByFileName(value));
}
}
//HUH? How can i NOT use the container??, in this case in the button_click
ILoader Loader = Container.GetInstance<Func<string, ILoader>>();
ILoader l2 = Loader(TextBox1.Text);
ISplitText Splitter = Container.GetInstance<ISplitText>();
编辑#1
好的,所以,在重新阅读评论并在互联网上查看更多示例之后,我想我可能终于明白了。问题是(我认为)是我对“更高层次”的思考不够。我试图在我的 winforms 应用程序中解决我的依赖关系,在表单已经构建之后,在表单本身中。到了现实时,为时已晚。我并没有将“表单本身”视为另一个对象,它需要将其依赖项注入其中。
所以我现在在我的 Program.cs 中引导:
static class Program
{
private static ServiceContainer Container;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Container = new ServiceContainer();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
BootStrapIOC Strap = new BootStrapIOC(Container);
Strap.Start();
//This magic line resolves EVERYTHING for me required by the Form
var form = Container.GetInstance<Form1>();
Application.Run(form);
//Application.Run(new Form1());
}
}
我现在的问题是,就winforms而言,我的思路现在是否正确。似乎更有意义,将我的方法更改为“更高”的链条并从 Program.cs 解决?
其次,我不确定这是否完全需要一个新问题,请告知,因为我是一个菜鸟。
我将如何设置工厂以返回对象的正确实例?原始评论之一表明这将是这种情况下的用法。让我们使用一个人为的例子。我需要一个对象,但直到运行时/用户输入才知道哪个对象。
我的想法:
BootStrap Container.Register();
工厂接口和实现:让我们也放入一些可选参数,因为我想知道这是否是正确/最好的方法?
public interface IFileViewerFactory
{
ILoader GetFileViewer(string FileName, string Directory = null, bool CreatingDirectory = false);
}
public class FileViewerFactory:IFileViewerFactory
{
public FileViewerFactory() { }
public ILoader GetFileViewer(string FileName, string Directory = null, bool CreatingDirectory = false)
{
if (CreatingDirectory == false)
{
if (Directory == null)
return new FileViewByFileName(FileName);
else
return new FileViewByDirectoryName(Directory, FileName);
}
else
return new FileViewByDirectoryNameCreateDirectoryOptional(Directory, FileName, CreatingDirectory);
}
}
形式:
public IFileViewerFactory FileViewerFactory { get; set; }
按钮点击:
ILoader FileLoader = FileViewerFactory.GetFileViewer(TxtBoxFileName.Text);
或者:
ILoader FileLoader = FileViewerFacotry.GetFileViewer(TxtBoxFileName.Text,TxtBoxDirectory.Text);
所以最后,我的问题是:
- 我的“更高层次”思维的新方式,以及从 Program.cs 引导现在是否正确
- 如何处理 LightInject 中的可选参数
- 我如何设置我的工厂是正确的方法吗?
- 让我们忘记工厂的琐碎性,而只是尝试研究问题的机制:)