System.IO.Abstractions 正在使用Adapter Pattern。这是用于某些没有任何抽象(抽象类或接口)的类型以便将它们与 DI 一起使用的技巧。由于无法向 .NET 中的现有类型添加抽象,因此创建了一个具有抽象(在本例中为抽象类)的包装器(适配器),以便用于松散耦合实现。
这里的问题是您没有使用包装器,而是直接使用实现。
IKernel ninject = new StandardKernel();
ninject.Bind<DirectoryInfoBase>().To<DirectoryInfoWrapper>()
.WithConstructorArgument("instance", new DirectoryInfo(@"C:\Somewhere\"));
但是,这里还有另一个问题 - DirectoryInfo 需要目录路径作为构造函数参数。因此,这意味着使用Abstract Factory 通常更有意义,因此可以在知道目录路径时在运行时创建它。在这种情况下,将工厂注入到您的服务中,然后在运行时调用该方法来创建实例更有意义。 System.IO.Abstractions 的作者在内部制作了工厂,但您也可以构建一个相同的工厂。
[Serializable]
public class DirectoryInfoFactory : IDirectoryInfoFactory
{
public DirectoryInfoBase FromDirectoryName(string directoryName)
{
var realDirectoryInfo = new DirectoryInfo(directoryName);
return new DirectoryInfoWrapper(realDirectoryInfo);
}
}
public class SomeService : ISomeService
{
private readonly IDirectoryInfoFactory directoryInfoFactory;
public SomeService(IDirectoryInfoFactory directoryInfoFactory)
{
if (directoryInfoFactory == null)
throw new ArgumentNullException("directoryInfoFactory");
this.directoryInfoFactory = directoryInfoFactory;
}
public void DoSomething()
{
// The directory can be determined at runtime.
// It could, for example, be provided by another service.
string directory = @"C:\SomeWhere\";
// Create an instance of the DirectoryInfoWrapper concrete type.
DirectoryInfoBase directoryInfo = this.directoryInfoFactory.FromDirectoryName(directory);
// Do something with the directory (it has the exact same interface as
// System.IO.DirectoryInfo).
var files = directoryInfo.GetFiles();
}
}
然后配置容器注入一个可以创建多个运行时实例而不是单个实例的工厂。
IKernel ninject = new StandardKernel();
ninject.Bind<IDirectoryInfoFactory>().To<DirectoryInfoFactory>();
但是 System.IO.Abstractions 的作者使用了另一个技巧来更进一步。他制作了一个Aggregate Service,它可以被注入并提供System.IO命名空间中的类型以松散耦合的方式提供的许多服务。
因此,与其创建自己的工厂,不如注入现有的 IFileSystem 服务,以获得对 System.IO 命名空间提供的几乎所有服务的访问权限。
public class SomeService : ISomeService
{
private readonly IFileSystem fileSystem;
public SomeService(IFileSystem fileSystem)
{
if (fileSystem == null)
throw new ArgumentNullException("fileSystem");
this.fileSystem = fileSystem;
}
public void DoSomething()
{
// The directory can be determined at runtime.
// It could, for example, be provided by another service.
string directory = @"C:\SomeWhere\";
// Create an instance of the DirectoryInfoWrapper concrete type.
DirectoryInfoBase directoryInfo = this.fileSystem.DirectoryInfo.FromDirectoryName(directory);
// Do something with the directory (it has the exact same interface as
// System.IO.DirectoryInfo).
var files = directoryInfo.GetFiles();
}
}
然后您将配置容器以注入 IFileSystem 以获得 System.IO 的所有功能。
IKernel ninject = new StandardKernel();
ninject.Bind<IFileSystem>().To<FileSystem>();