是否需要使用构造函数注入检查空值?

时间:2018-10-18 18:36:46

标签: c# dependency-injection .net-core constructor-injection null-check

我正在使用.NET Core构造函数注入。 在同事的代码审查中,他提出了一个问题,即我是否应该检查控制器中注入的依赖项是否为空值。

由于框架负责创建服务的实例,因此在我看来,它将处理所有错误,并且永远不会将空值依赖项传递给构造函数。我对此没有任何事实证据,所以我想知道是否有必要进行空检查。

例如,我是否应该检查以下代码中的“ myService”是否为空? (假设代码配置为使用DI)

public class MyController
{
    private readonly IMyService _myService;

    public MyController(IMyService myService) 
    {
        _myService = myService;
    }
}

3 个答案:

答案 0 :(得分:4)

  

是否需要使用构造函数注入检查空值?

要视情况而定。

  • 您和(可能)某些队友在(很幸运)代码审查环境中使用的此内部代码吗?

不要。这不是必需的。该框架不允许这样做。

  • 此代码是在公共库中供多个人使用,还是在依赖注入之后才真正使用?

然后执行。手动实例化会在某个地方导致NullReferenceException,并且很难找到。

就是这样,使用类似这样的内容:

public MyController(IMyService myService) 
{
    if (myService == null)
    {
        throw new ArgumentNullException(nameof(myService));
    }

    _myService = myService;
}

这是一张非常便宜的支票,如果有人出于某种原因通过null,则更容易追踪。

答案 1 :(得分:1)

为避免明显的干扰,在构造函数中执行null检查没有任何危害。


有两种从框架中获取DI服务的方法。

第一个是GetService<T>()。如果尚未注册该服务,则此 将返回空值。

第二个是GetRequiredService<T>()。如果找不到该服务,此will throw an exception

如果这两种方法无法完全实例化您要的服务,则它们都会引发异常。

class Program
{
    static void Main(string[] args)
    {
        var services = new ServiceCollection()
            .AddTransient<IServiceB, ServiceB>()
            .BuildServiceProvider();

        var servB = services.GetService<IServiceB>();
    }
}

public interface IServiceA { }
public interface IServiceB { }

public class ServiceA : IServiceA { }
public class ServiceB : IServiceB { public ServiceB(IServiceA a) { } }

在此示例中,ServiceB需要一个IServiceA,但是A未添加到依存关系图中。最后一个方法将引发异常:

  

System.InvalidOperationException:“在尝试激活'DITests.ServiceB'时,无法解析类型为'DITests.IServiceA'的服务。'

如果我改而services.GetService<IServiceA>(),我将得到一个空值。


您可以通过查看GitHub source code来亲自查看。当调用这两种方法时,它最终都会变成CreateConstructorCallSite方法的方式。如果无法解析您的类型的依赖项,则会引发异常。


对于ASP.Net Core MVC,它使用GetRequiredService<>()从DI图中获取控制器。


总而言之,不,如果您使用纯Microsoft DI框架,则无需 对构造函数中的DI对象执行空检查。正如Camilo Terevinto所说,该框架不允许这样做。

如您的帖子中所述,我还没有看到Microsoft书面文件明确表明您不需要

如果要传递IServiceProvider作为构造函数参数,并且要从构造函数中解析服务,那么然后您将需要进行空检查。

答案 2 :(得分:0)

来自Camilo Terevintogunr2171的这些答案对您都有好处。

通常,我们可以讨论将一个例外与另一个例外(从NullReferenceExceptionArgumentNullException)进行交易还是完全达成交易。问问自己,后者是否为您带来任何新信息。我的拙见是

一种或另一种方式,您会立即知道特定情况下的问题是什么,在哪里。在控制器的构造函数中执行null检查只是开销。这就是Camilo Terevinto的答案失败的地方。您永远不会自己启动它,也永远不会将其放入任何库代码中。 .NET Core中的内置DI服务旨在永远不要在构造函数中注入null

gunr2171的答案是关于DI的完全不同的方面。这是一个有效的答案,但针对另一个问题。


超过80%的DI使用是构造函数注入。这些注入大多数与MVC控制器和Razor页面中的视图模型有关。 MVC是ASP.NET Core 3.x和replaced by Razor Pages中已弃用的设计模式,但逻辑几乎相同。

任何可用的控制器至少需要三个依赖项:

  1. 记录器
  2. 映射器
  3. 存储库

在这里推我,您不想输入:

_logger = logger ?? throw new ArgumentNullException(...)
_mapper = mapper ?? throw new ArgumentNullException(...)
_repository = repository ?? throw new ArgumentNullException(...)

针对您拥有的每个控制器/视图模型。

绝对有更好的方法来做到这一点。我个人的建议是this,重复一个问题。