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