为什么我不能将System.IO.Abstractions与Ninject绑定?

时间:2015-01-10 09:41:11

标签: c# .net dependency-injection ninject

我只是学习使用Ninject的依赖注入,并使用System.IO.Abstractions来抽象文件系统。我正在尝试使用Ninject将DirectoryInfoBase绑定到DirectoryInfo,如下所示:

IKernel ninject = new StandardKernel();
ninject.Bind<DirectoryInfoBase>().To<DirectoryInfo>();

但是我收到了错误

  

错误1类型&#39; System.IO.DirectoryInfo&#39;不能用作类型参数&#39; TImplementation&#39;在泛型类型或方法中,Ninject.Syntax.IBindingToSyntax.To()&#39;。没有从System.IO.DirectoryInfo&#39; System.IO.DirectoryInfo&#39;中隐式引用转换。到&#39; System.IO.Abstractions.DirectoryInfoBase&#39;。 C:\ Users \ Trevor \ Dropbox \ Code \ PhotoOrganiser \ PhotoOrganiser \ Program.cs 13 13 PhotoOrganiserApp

我在这里缺少什么?我认为像这样的图书馆的目标是能够执行这些类型的任务吗?

2 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:0)

您无法使用该特定约束,因为DirectoryInfo不会继承或实施DirectoryInfoBase

您可能会因使用

DirectoryInfo投射到DirectoryInfoBase而被误导
DirectoryInfo dirInfo;
DirectoryInfoBase dirInfoBase = (DirectoryInfoBase)dirInfo;

但这只是因为DirectoryInfoBase中的隐式强制转换操作符:

public static implicit operator DirectoryInfoBase(DirectoryInfo directoryInfo)
{
    if (directoryInfo == null)
        return null;
    return new DirectoryInfoWrapper(directoryInfo);
}

我不熟悉System.IO.Abstractions,但为什么不注入IFileSystem,就像

中的示例一样
ninject.Bind<IFileSystem>().To<FileSystem>();

如果您有IFileSystem,则可以

fileSystem.DirectoryInfo.FromDirectoryName(directoryName)

获取DirectoryInfoBase个对象。