1

关于此代码来配置服务:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDb, Db>();
    services.AddControllers();
}

特别是这一行:

services.AddSingleton<IDb, Db>();

如果 AddSingleton 不是像代码片段中那样是一个具有 0 参数的通用函数,是否会有任何语义差异

AddSingleton(Type type1, Type type2);
4

3 回答 3

3

的实现AddSingleton<T1, T2>看起来像这样:

public static IServiceCollection AddSingleton<TService, TImplementation>(this IServiceCollection services)
    where TService : class
    where TImplementation : class, TService
{
    if (services == null)
    {
        throw new ArgumentNullException(nameof(services));
    }

    return services.AddSingleton(typeof(TService), typeof(TImplementation));
}

如您所见,它实际上调用了AddSingleton传递两种类型的另一个重载。因此,从语义上讲,您是否执行AddSingleton<IDb, Db>()或. 都没有区别AddSingleton(typeof(IDb), typeof(Db))。两次调用都将产生完全相同的结果。

有一个通用重载的原因是它感觉好多了。泛型方法优于传递类型,因为您可以更轻松地编写它。因此,您更有可能看到通用用法。

此外,您可以为泛型类型参数添加约束,这可能会添加一些编译时检查。在这种特殊情况下,约束如下所示:

where TService : class
where TImplementation : class, TService

除了这两种类型都必须是引用类型之外,还有一个额外的要求是TImplementation继承自TService. 这确保您可以在预期TImplementationa 的地方实际使用类型的实例。TService这也是里氏替换原则背后的理念。通过具有类型约束,此检查将在编译时进行验证,因此您可以确保这将在运行时工作,因为如果您使用其他重载则无法保证这一点。

不用说,AddTransient<>AddScoped<>以相同的方式处理它们各自的非泛型重载。

于 2020-05-08T15:25:11.957 回答
2

从语义上讲,两者的含义相同。但是,使用泛型方法的一个优点是可以在编译时强制对传递给方法调用的类型进行约束。

此方法的文档中,请注意约束where TImplementation : class, TService. 编译器可以在编译时检查该Db类型是否实现IDb并且是一个类。

使用非泛型方法,这在编译时是不可能的。(相反,它可以在运行时检查,并且可能会引发运行时错误。)

于 2020-05-08T15:16:48.047 回答
1

如果你搜索微软的文档,你会发现所有 Addsingleton 的工作方式都是一样的,唯一不同的是条目:

AddSingleton:将 TService 中指定类型的单例服务添加到指定的 IServiceCollection 中,实现类型在 TImplementation 中指定。

AddSingleton(IServiceCollection, Type, Type):将 serviceType 中指定类型的单例服务与 implementationType 中指定的类型的实现添加到指定的 IServiceCollection。

文档来源

于 2020-05-08T15:20:16.730 回答