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

Jos*_*VdM 1 c# dependency-injection constructor-injection null-check .net-core

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

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

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

public class MyController
{
    private readonly IMyService _myService;

    public MyController(IMyService myService) 
    {
        _myService = myService;
    }
}
Run Code Online (Sandbox Code Playgroud)

gun*_*171 6

为了避免显而易见的问题,在构造函数中进行空检查并没有什么坏处。


有两种方法可以从框架获取 DI 服务。

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

第二个是GetRequiredService<T>()。如果找不到服务,这将引发异常。

如果这两种方法无法完全实例化您所请求的服务,则会抛出异常。

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) { } }
Run Code Online (Sandbox Code Playgroud)

在此示例中,ServiceB需要IServiceA,但未A添加到依赖关系图中。最后一个方法会抛出异常:

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

如果我这样做services.GetService<IServiceA>(),我会得到一个空值。


您可以通过查看GitHub 源代码来亲自了解这一点。当调用任一方法时,它最终都会到达该CreateConstructorCallSite方法。如果无法解析您的类型的依赖关系,则会引发异常。


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


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

正如您在帖子中所指出的,我还没有看到微软的书面文档明确表示您不需要

如果您将 an 作为构造函数参数传递IServiceProvider,并且要从构造函数内解析服务,需要执行 null 检查。


SO *_*ood 5

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

这取决于。

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

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

  • 这段代码是在公共库中,由多个人使用还是在依赖注入之后没有实际使用?

然后去做 手动实例化会在某处导致NullReferenceException,并且很难找到它们。

就是说,使用这样的东西:

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

    _myService = myService;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 另外,使用较新的 C# 语言选项,您可以这样做;(当然就在ctor内部)`_httpContextAccessor = httpContextAccessor ?? 抛出 new ArgumentNullException(nameof(httpContextAccessor));` 有点东西。 (2认同)