简体   繁体   中英

Question about C# Pattern in ASP.NET Core When Adding Services in Startup.cs

With regards to this code to configure services:

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

and specifically the line:

services.AddSingleton<IDb, Db>();

would there be any semantic difference if AddSingleton, instead of being a generic function with 0 parameters as in the code snippet, was instead

AddSingleton(Type type1, Type type2);

The implementation of AddSingleton<T1, T2> looks like this:

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));
}

As you can see, it literally calls the other overload of AddSingleton that passes two types instead. So semantically, it makes no difference whether you do AddSingleton<IDb, Db>() or AddSingleton(typeof(IDb), typeof(Db)) . Both calls will result in the exact same result.

The reason that there is a generic overload is that it feels a lot nicer. Generic methods are preferred over passing types because you can just write it easier. So you are more likely to see the generic usage.

In addition, there is the benefit that you can add constraints to the the generic type arguments which may add some compile time checks. In this particular case, the constraints look like this:

where TService : class
where TImplementation : class, TService

Apart from the fact that both types are required to be reference types, there is the additional requirement that TImplementation inherits from TService . This makes sure that you can actually use instances of type TImplementation in places where a TService is expected. That is also the idea behind the Liskov Substitution Principle . By having a type constraint, this check is being verfied at compile time , so you can be sure that this will work at runtime where as this is not guaranteed if you use the other overload.

Needless to say, AddTransient<> and AddScoped<> work in the same way over their respective non-generic overloads.

Semantically, both mean the same thing. However, one advantage of using the generic method is that constraints on the types passed to a call to the method can be enforced at compile time.

In the documentation for this method , notice the constraint, where TImplementation: class, TService . The compiler can check at compile time that the Db type implements IDb and is a class.

With the non-generic method, this is not possible at compile time. (Instead, it could be checked at runtime, and a runtime error could be thrown.)

If you search Microsoft's documentation, you will find that all Addsingleton work the same, the only thing that differs is the entry:

AddSingleton : Adds a singleton service of the type specified in TService with an implementation type specified in TImplementation to the specified IServiceCollection.

AddSingleton(IServiceCollection, Type, Type): Adds a singleton service of the type specified in serviceType with an implementation of the type specified in implementationType to the specified IServiceCollection.

documentation source

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM