我想知道是否还有,如何使用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);
}
}
然后在代码中的某个地方,我将通过内核获得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
}
}
这样可以正常工作,问题是,只有文件路径参数的名称保持不变,它才会起作用。让我们说有人会决定,filePath
是不必要的,并将其重命名为path
。代码本身仍然会编译,但在有人实际调用Bar()
方法之前不会导致任何错误。
有什么方法可以防止这种情况发生吗?
我真的不想公开filePath
。我仍然希望它作为构造函数的参数传递。我不想更改FileCheck()
的签名以接受filePath
作为参数,我甚至不想将filePath
更改为可公开访问的字段。
答案 0 :(得分:5)
这会将DI容器滥用为service locator,而另一个原因为什么服务定位器被视为anti-pattern。
如果使用依赖注入模式,则不会发生您的设想示例。重构您的示例以使用依赖注入,它看起来像这样:
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);
}
}
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
}
}
在composition root,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();
}
}
如果需要检查是否存在filePath
参数,可以使用guard子句进行此检查。您只需要使用dependency injection pattern,并通过方法参数传递运行时数据(文件路径)。
如果您希望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();
}
}
答案 1 :(得分:1)
如果您的参数仅在运行时知道,您可以注入工厂并使用它创建FileExistenceChecker
:
public interface IFileExistenceCheckerFactory
{
IFileExistenceCheck Create(string path);
}
...
var kernel = new StandardKernel();
kernel.Bind<IFileExistenceCheck>().To<FileExistenceChecker>();
kernel.Bind<IFileSystem>().To<FileSystem>();
kernel.Bind<IFileExistenceCheckerFactory>().ToFactory(() => new TypeMatchingArgumentInheritanceInstanceProvider());
var factory = kernel.Get<IFileExistenceCheckerFactory>();
var checker = factory.Create("SomeMagicString");
var fileExist = checker.FileExist();
然后,即使参数名称不匹配,TypeMatchingArgumentInheritanceInstanceProvider
也要确保参数与其类型匹配。