繁体   English   中英

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

[英]Why can't I bind System.IO.Abstractions with Ninject?

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

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

但是我得到了错误

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

我在这里想念什么? 我认为诸如此类的库的目标是能够执行此类任务?

System.IO.Abstractions正在使用适配器模式 对于某些没有任何抽象的类型(抽象类或接口)以便与DI一起使用,这是一个技巧。 由于无法将抽象添加到.NET中的现有类型,因此将创建具有抽象(在本例中为抽象类)的包装器(适配器),以用于松散耦合实现。

这里的问题是您没有使用包装器,而是直接使用实现。

IKernel ninject = new StandardKernel();
ninject.Bind<DirectoryInfoBase>().To<DirectoryInfoWrapper>()
    .WithConstructorArgument("instance", new DirectoryInfo(@"C:\Somewhere\"));

但是,这里还有另一个陷阱-DirectoryInfo需要目录路径作为构造函数参数。 因此,这意味着使用抽象工厂通常更有意义,这样可以在运行时在知道目录路径的情况下创建它。 在这种情况下,将工厂注入服务中,然后在运行时调用该方法来创建实例更有意义。 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的作者还有另一个技巧,可以使它更进一步。 他制作了一个聚合服务 ,可以将其注入并提供以松散耦合方式在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>();

您不能使用该特定绑定,因为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对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM