通过构造函数使用依赖项注入时,我总是需要在将实例传递给内部属性之前检查空值。 e.g。
public UserManager(User user, IStateManager stateManager)
{
if(user == null) throw new arguementexception("user");
if(statemanager == null) throw new arguementexception("stateManager");
_user = user;
_stateManager = statemanager;
}
在每个控制器/类上重复此模式似乎是重复的。有没有更好的方法来处理这个?顺便说一句不同的控制器将有不同的构造函数初始化程序。我正在使用Simple Injector进行DI。
答案 0 :(得分:12)
这是重复的代码,但它几乎不会成为一个问题,因为这会导致代码库中的彻底改变吗?您是否需要更改许多支票?几乎不。请查看更详细的this blog post。
老实说,当涉及到我的injection constructors时,我几乎再也不会添加那些空检查,因为我知道我的DI容器在自动连接这些类型时不会将空引用注入我的构造函数中。这使我免于编写所有这些空检查。
有些人可能会说我现在用我的DI容器编写代码,但我会反对。我只是编写了解决我问题所需的最少量代码。在我的情况下,添加这些空检查对我没有帮助。
但请注意,如果我正在为可重用的库编写代码,我绝对会编写那些空检查,因为我不知道是谁在调用该代码。对于不用作注入构造函数(消息,实体,值类型,DTO)的构造函数,我实际上要添加这些检查。但是这里有一些想法如何使这更好一点:
你可以像这样添加一个很好的帮助方法:
public UserManager(User user, IStateManager stateManager)
{
Requires.IsNotNull(user, "user");
Requires.IsNotNull(statemanager, "statemanager");
_user = user;
_stateManager = statemanager;
}
然而,这并没有真正帮助减少重复代码,尽管它确实减少了生成的机器代码的实际大小(但这几乎不是问题)。因此,您可以使此方法返回如下值:
public UserManager(User user, IStateManager stateManager)
{
_user = Requires.IsNotNull(user, "user");
_stateManager = Requires.IsNotNull(statemanager, "statemanager");
}
或者......使用C#6.0:
public UserManager(User user, IStateManager stateManager)
{
_user = Requires.IsNotNull(user, nameof(user));
_stateManager = Requires.IsNotNull(statemanager, nameof(statemanager));
}
您可以按如下方式实施此方法:
public static class Requires {
public static T IsNotNull<T>(T instance, string paramName) where T : class {
// Use ReferenceEquals in case T overrides equals.
if (object.ReferenceEquals(null, instance) {
// Call a method that throws instead of throwing directly. This allows
// this IsNotNull method to be inlined.
ThrowArgumentNullException(paramName);
}
return instance;
}
private static void ThrowArgumentNullException(paramName) {
throw new ArgumentNullException(paramName);
}
}
我们只希望C#8添加non-nullable reference types。这将允许我们将代码减少到以下内容:
public UserManager(User! user, IStateManager! stateManager)
{
_user = user;
_stateManager = statemanager;
}
如果C#在定义类型方面有更好的方法(这是C#6的提案,但从未添加到最终版本中),以下内容可能会更简化类型定义:
public class UserManager(User! user, IStateManager! stateManager)
{
}
答案 1 :(得分:1)
我使用静态方法.ThrowIfNull,如果为null,将抛出具有正确参数名称的ArgumentNullException。
public MyClass
{
public MyClass(DependencyType1 firstDependency, DependencyType2 secondDependency)
{
Arguments.ThrowIfNull(firstDependency, secondDependency);
_firstDependency = firstDependency;
_secondDependency = secondDependency;
}
}
public static class Arguments
{
public static void ThrowIfNull(params object[] args)
{
for (var i = 0; i < args.Length; i++)
{
if (args[i] != null
&& args[i].GetType() == typeof(ArgumentAction)
&& (ArgumentAction)args[i] == ArgumentAction.Skip) continue;
if (args[i] == null) throw GetArgumentNullException(i);
}
}
private static ArgumentNullException GetArgumentNullException(int argIndex)
{
var frame = new StackFrame(2, false);
var method = frame.GetMethod();
var args = method.GetParameters();
var name = args[argIndex].Name;
return new ArgumentNullException(name);
}
public enum ArgumentAction
{
Undefined = 0,
Skip
}
}
答案 2 :(得分:1)
事实上,此类检查会产生0业务价值,但会在代码中产生额外的噪音。同事开发人员会感到困惑,他们将不得不花费额外的努力来了解你为什么要进行所有这些检查。 通常,当无法解析依赖关系时,IoC容器将抛出错误。即使你的容器没有抛出,你也会在执行期间稍后获得null引用异常。最好添加Debug.Assert语句,这表明你不确定这里到底发生了什么,但有时候你有空