Ninject构造函数参数检查参数存在

Kaj*_*ama 3 .net c# dependency-injection ninject inversion-of-control

我想知道无论如何,如何使用Ninject进行参数存在检查?

我指的是:让我们有一个理论类和一个接口:

public interface IFileExistenceCheck
{
    bool FileExist();
}

public class FileExistenceChecker : IFileExistenceCheck
{
    private readonly string filePath;
    private readonly IFileSystem fileSystem;

    public FileExistenceChecker(IFileSystem fileSystem, string filePath)
    {
        this.fileSystem = fileSystem;
        this.filePath = filePath;
    }

    public bool FileExist()
    {
        return this.fileSystem.File.Exists(this.filePath);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在代码中的某个地方,我将IFIleExistenceCheck通过内核获得一个接口实例,如下所示:

public class Foo()
{
    public void Bar()
    {
        // do something
        var filePath = SomeMagicString;

        var filePathArgument = new ConstructorArgument("filePath", filePath); // <- This part I do not really like
        var checker = Kernel.Get<IFileExistenceCheck>(filePathArgument);
        var fileExist = checker.FileExist();

        // do the rest of code
    }
}
Run Code Online (Sandbox Code Playgroud)

这将工作得很好,问题是,只有文件路径参数的名称保持不变,它才会起作用.让我们说有一天会有人决定,这filePath是不必要的,只是重命名path.代码本身仍然会编译,但是在有人实际调用该Bar()方法之前不会导致任何错误.

有什么方法可以防止这种情况发生吗?

我真的不想曝光filePath.我仍然希望它作为构造函数的参数传递.我不想更改签名作为参数FileCheck()接受filePath,我甚至不想更改filePath为可公开访问的字段.

Nig*_*888 5

这是滥用DI容器作为服务定位器,而服务定位器的另一个原因被认为是反模式.

如果您使用依赖注入模式,那么您的设计示例将不会发生.重构您的示例以使用依赖注入,它看起来像这样:

public interface IFileExistanceCheck
{
    bool FileExist(string filePath);
}

public class FileExistanceChecker : IFileExistanceCheck
{
    private readonly IFileSystem fileSystem;

    public FileExistanceChecker(IFileSystem fileSystem)
    {
        if (fileSystem == null)
            throw new ArgumentNullException(nameof(fileSystem));
        this.fileSystem = fileSystem;
    }

    // Pass runtime data through the method parameters!
    public bool FileExist(string filePath)
    {
        // Prevent an empty file path from being used
        if (string.IsNullOrEmpty(filePath))
            throw new ArgumentNullException(nameof(filePath));

        return this.fileSystem.File.Exists(filePath);
    }
}
Run Code Online (Sandbox Code Playgroud)

Foo类

public class Foo
{
    private readonly IFileExistanceCheck fileExistanceCheck;

    public Foo(IFileExistanceCheck fileExistanceCheck)
    {
        if (fileExistanceCheck == null)
            throw new ArgumentNullException(nameof(fileExistanceCheck));
        this.fileExistanceCheck = fileExistanceCheck;
    }
    public void Bar()
    {
        // do something
        var filePath = SomeMagicString;

        var fileExist = fileExistanceCheck.FileExist(filePath);

        // do the rest of code
    }
}
Run Code Online (Sandbox Code Playgroud)

组合根目录下,Ninject将它们组合在一起并开始Foo运行.

class Program
{
    static void Main(string[] args)
    {
        // Begin composition root

        var kernel = new StandardKernel();

        kernel.Bind<IFileSystem>().To<FileSystem>();
        kernel.Bind<IFileExistanceCheck>().To<FileExistanceChecker>();

        var app = kernel.Get<Foo>();

        // End composition root

        app.Bar();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果需要检查filePath参数是否存在,可以使用guard子句进行此检查.您只需要使用依赖项注入模式,并通过方法参数传递运行时数据(文件路径).

如果您希望filePath它是在应用程序启动时通过构造函数传递的配置值,那么使用检查文件存在的服务似乎毫无意义.在这种情况下,您应该在允许应用程序运行之前检查文件是否存在.

class Program
{
    static void Main(string[] args)
    {
        var filePath = "SomeFileThatShouldExist.txt";

        // Check configuration
        if (!File.Exists(filePath))
            throw new InvalidOperationException("Invalid configuration");

        // Begin composition root

        var kernel = new StandardKernel();

        kernel.Bind<Foo>().To(new Bar(filePath));
        // Register other services...

        var app = kernel.Get<Foo>();

        // End composition root

        app.Bar();
    }
}
Run Code Online (Sandbox Code Playgroud)