在依赖注入中检查 null 的更好方法

2024-02-17

通过构造函数使用依赖项注入时,我始终需要在将实例传递给内部属性之前检查是否为空。例如

public UserManager(User user, IStateManager stateManager)
{
    if(user == null) throw new ArgumentNullException;
    if(statemanager == null) throw new ArgumentNullException("stateManager");

    _user = user;
    _stateManager = statemanager;
} 

在每个控制器/类上重复此模式似乎是重复的。有更好的方法来处理这个问题吗?顺便说一句,不同的控制器将有不同的构造函数初始化程序。我正在为我的 DI 使用简单注入器。


这是重复的代码,但它几乎不会成为问题,因为这会导致彻底改变您的代码库吗?您是否需要更改其中许多支票?几乎不。看一眼这篇博文 https://blog.ploeh.dk/2014/08/07/why-dry/这有更多细节。

说实话,当谈到我的注入构造函数 https://blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple/,我几乎不再添加这些 null 检查,因为我知道我的 DI 容器在自动装配这些类型时不会将 null 引用注入到我的构造函数中。这使我根本不用编写所有这些空检查。

有些人可能会说我现在在编写代码时考虑了 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不可为 null 的引用类型 https://github.com/dotnet/roslyn/issues/227,默认情况下可以将引用类型设置为不可空:

public UserManager(User user, IStateManager stateManager)
{
    _user = user;
    _stateManager = statemanager;
}

请注意,这只会导致编译时强制,而不是运行时强制。所以不会抛出异常。

这可能会随着 C# 9 的改变而改变。有人建议使用! symbol:

public UserManager(User user!, IStateManager stateManager!)
{
    _user = user;
    _stateManager = statemanager;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在依赖注入中检查 null 的更好方法 的相关文章

随机推荐

  • int a=1, b=a++; 是否调用未定义的行为?

    Does int a 1 b a 调用未定义的行为 初始化之间没有序列点介入a及其在初始化程序中的访问和修改b 但据我所知 初始化不是对象的 修改 指定初始值设定项来给出对象的 初始值 根据 6 7 8 初始化 第 8 段 初始化器指定存储
  • 可以直接用二进制写程序吗?我怎样才能让计算机执行它?

    我知道这可能看起来很奇怪并且自找麻烦 但我认为体验古代程序员以前经历过的事情是很有趣的 那么如何执行仅用二进制编写的程序呢 假设我知道我在做什么 当然不使用汇编 我只想写一系列像111010111010101010101并执行它 那么我该怎
  • 如何使用 WPF 后台工作者

    在我的应用程序中 我需要执行一系列初始化步骤 这些步骤需要 7 8 秒才能完成 在此期间我的 UI 变得无响应 为了解决这个问题 我在一个单独的线程中执行初始化 public void Initialization Thread initT
  • -XX:+PrintGC 和 -verbose:gc 之间的区别

    我想了解以下之间的区别 XX PrintGC and verbose gc显然这些看起来很相似 本文没有列出verbose gc http www oracle com technetwork articles java vmoptions
  • 如何使用 TFS 构建服务器中的模板运行 NUnit 测试

    我需要使用 TFS Server 2010 中的工作流模板运行 nUnit 测试 我找到了下面所有解释如何配置构建服务器的参考资料 http morten lyhr dk 2008 05 using nunit and ncover wit
  • Error62:在 VBA 脚本中输入文件末尾以搜索目录中包含关键字的文件

    希望有人能够指出我误入歧途的地方 我创建了一个脚本来搜索目录中的所有文件 并返回包含已输入到单元格中的特定关键字的任何文件的文件路径和文件名 该脚本似乎适用于我在桌面上设置的测试文件夹 但是当我尝试搜索包含更多文件的另一个目录时 我收到 输
  • 自定义活动设计器中的参数验证

    我在验证自定义活动的设计器中正常工作时遇到问题 重现该行为的最简单示例如下 我有一个自定义 WF4 活动 其中包含存储在字典中的动态参数集合 Designer typeof DictionaryActivityDesigner public
  • Rand Index函数(聚类性能评估)

    据我所知 python 中没有可用于 Rand Index 的软件包 而对于调整后的 Rand Index 您可以选择使用sklearn metrics adjusted rand score labels true labels pred
  • 获取自己的标头 $Resource AngularJs

    我有 GO 语言的 REST API 和 Angularjs 的前端 但是当我以 Angular 形式获取资源时 我的自定义标头不存在 控制器 Persons query function data headerGetter status
  • 共享元素转换不适用于 Android 中的添加片段

    我观察到共享元素转换仅适用于 替换 方法 有什么方法可以使其与 add 方法一起使用 None
  • 使用php通过android volley发送图像[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在用数据和图像从 android 到 php 发布帖子 我正在 android 中缩小图像并对其进行 base 64 编码 当通过齐射
  • [Firestore][I-FST000001] 无法到达 Firestore 后端

    我已经配置了 Firebase Firestore 但 Firestore 曾返回此错误 Firestore I FST000001 无法到达 Firestore 后端 我正在使用 Swift 4 和 Xcode 9 有什么想法吗 2018
  • 程序部署后 JNI 找不到共享库

    将导出的 Java 项目从开发计算机移至生产环境后 我遇到了麻烦 java项目 一个Eclipse插件 有一个我写的JNI库 它依赖于一个开源库 而这个库又依赖于Boost 我在我的 SLES11 机器上编译了所有内容 包括 Boost 并
  • 使用节点 8 async/await 和 Angular 6 调试量角器时出错

    我无法让量角器调试器与节点 8 async await 和 Angular 6 一起使用 以旧方式返回到节点 7elementExplorer browser pause and browser debugger 不是一个选择 也因为在未来
  • Try and Catch on TSQL - catch 不捕获

    我有一个存储过程似乎没有正确记录其错误 代码出错 但 catch 块似乎没有生效 try 块相当长 但错误部分很简单并且就在最后 所以我已经精确说明了这一点 BEGIN TRY insert into tbl X select from t
  • 为什么我在此文件夹上收到 UnauthorizedAccessException?

    据 MSDN 报道 目录 存在 https msdn microsoft com en us library system io directory exists v vs 110 aspx如果目录不可访问 则应返回 false 我有一条路
  • 使用 jQuery 无法找到动态添加的 HTML 元素

    我正在 HTML 中实现一个树浏览器 单击节点时 我调用一个添加该节点的子元素的函数 到目前为止 一切都很好 我现在想立即调用其中一个子元素的单击处理程序来展开它 我的问题是 jQuery 找不到刚刚添加的子元素 当我在调试器中单步调试时
  • x/y 坐标的排序向量

    我有一个向量 u32 u32 表示 10 x 10 网格上坐标的元组 坐标未排序 因为标准sort函数也没有产生我想要的结果 我为他们编写了一个这样的排序函数 vec sort by a b if a 0 gt b 0 return Ord
  • 使用 SQL 批量更新记录

    我在 SQL Server 2008 环境中有两个表 其结构如下 Table1 ID DescriptionID Description Table2 ID Description Table1 DescriptionID 映射到Table
  • 在依赖注入中检查 null 的更好方法

    通过构造函数使用依赖项注入时 我始终需要在将实例传递给内部属性之前检查是否为空 例如 public UserManager User user IStateManager stateManager if user null throw ne