6

I understand difference between static class and singleton, importantly that singleton can be instantiated once where as static class doesn't need an instance.

This question is with the perspective of a .NET MVC project, to help me make decision between using either of them.

So assuming I have Class(es) with methods like the examples given below:

  1. I have a method like ConvertMeterToMiles(int mtr), where there is no dependency injected.

  2. Or a method like SendEmail(str eaddress), where there is no dependency injected but it instantiates new SMTPClient... followed by disposing the SMTPClient in the finally

Assuming I want to put the method into utility service class, then should I create a static class or singleton (ofcource with dependency injection)?

I understand there is no point of scoped or transient because there is no benefit to have new instances.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
variable
  • 8,262
  • 9
  • 95
  • 215
  • 1
    Best practice is to define a non static class and then utilize dependency injection to manage the lifecycle, https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-6.0#lifetime-and-registration-options. – Lex Li Jan 05 '22 at 14:41
  • If your method instantiates an `SMTPClient` it has a dependency, but you don't make it visible to the outer world. Instead it should get that thing via ctor injection. If one or both of this classes should have transient, scoped or singleton lifetime has to be decided when building the container. – Oliver Jan 13 '22 at 08:29
  • And if you have methods only working on their given parameters, producing always the same result when the same parameters are given, this is named *pure functions* and these can be make static. If their scope should be public, private or protected is another question. – Oliver Jan 13 '22 at 08:31
  • Another tip: When using DI, the default case should always be to use transient classes. Only if a class holds some internal state, that needs a longer lifetime, than think about using another scope. Don't be afraid about performance issues, because "it is not needed, to instantiate this class over and over again". This is premature optimization. Only take care if you can measure a performance problem and then think about other class lifetimes. – Oliver Jan 13 '22 at 08:36
  • If you have a method that has no dependencies and no resources(smtpClient) used then put it as static class. Static methods dont use 'this' parameter when instantiated:(Check the IL code using ildsm to see the difference). If you have a resource use during the method call then it is best to use disposable classes by inheriting Disposable and register this class as scoped or transient as it is MVC app and you would need instance when the API is called. – Santosh Karanam Jan 14 '22 at 17:55

7 Answers7

4

Adding a singleton via dependency injection instantiates an instance of the requested class. A static class cannot be instantiated, and so you simply access its methods from elsewhere, based on the static class's access modifiers.

Pierre Plourde
  • 708
  • 4
  • 12
  • But I want to ask from the perspective of a web api, since each request is handled by a different thread, then what is the difference between creating an utility class as singleton versus static class. – variable Jan 05 '22 at 15:33
  • 1
    Hi @variable, Web API or other project type is not relevant: that is the difference, one is instantiated, the other is not. – Pierre Plourde Jan 05 '22 at 15:53
  • I know that part, I want to understand this concept from perspective of web api – variable Jan 05 '22 at 16:05
  • 3
    Web API has nothing to do with anything here. Threading doesn't really come into play here either. All that matters is, do you need the class to be instantiated, or not? That's up to you and your design. – glenebob Jan 05 '22 at 16:13
  • I have updated question with examples. – variable Jan 12 '22 at 20:41
  • @variable based on your updated question, the answer is still the same: it depends. This isn't to try to weasel out of answering, but based on the information given, there really is no difference between the two. Other considerations might make one more practical than the other, though, including whether you are looking to store data or just have processing, whether you want different copies of the data, etc. – Pierre Plourde Jan 13 '22 at 14:26
  • you answer is only defines what are they, but the context is pretty much important on whether you use static or singleton and OP's question asks about the use in web api perpective. – Yılmaz Durmaz Jan 16 '22 at 09:30
  • With respect, @YılmazDurmaz, the choice of UI (API, MVC, Razor, Blazor, etc.) does not matter, DI is DI, and the choice is whether one needs to have the class instantiated or not. The OP did not give enough information about the use of the class to make a determination of which is best in that particular circumstance. As with many things, the answer is quite often "it depends". – Pierre Plourde Jan 16 '22 at 22:23
4

I would say that in context of your application the difference boils down to using DI or not and who is controlling lifetime/instantiation/injection.

Moving some functionality into some static helper class can be pretty fine if it should not differ based on environment or some other source of variability (another thing to consider - if the method is pure or not, i.e. pure functions usually can be good candidates too). For example ConvertMeterToMiles seems to be a fine candidate for such handling.

SendEmail on the other hand does not seem to be one - there can be environments where you don't want to send emails (test for example), or in future you anticipate having multiple implementations (or need to reimplement it) for this functionality (for example for some contexts email sending can be deferred using queue for example which will be handled by some background worker or another service). In this case you can highly leverage existence of DI and having encapsulated this functionality and hidden it behind contract (also I would say that handling SMTPClient settings is cleaner when they are registered in DI and resolved for encapsulated implementation).

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • 1
    I'm referring to online example of mailkit's SmtpClient and I see them Newing up the SmtpClient and disposing in finally block rather than injecting it via DI. Maybe this is to ensure that dispose of SmtpClient is immediate rather than relying on the DI frameworks dispose? – variable Jan 14 '22 at 04:10
  • Secondly, if the utility class is registered as singleton, then I have to new up the SmtpClient otherwise I will have to declare both the class and SmtpClient to be scoped. Correct? – variable Jan 14 '22 at 04:35
  • @variable I'm not suggesting to register `SmtpClient` in the DI but only settings for it. My post is about classes containing example methods, so in this case newing/disposing `SmtpClient` is implementation detail and it is fine to control `SmtpClient` lifetime outside of the DI container (I would argue that having `SmtpClient` registered can cause issues in some scenarios). – Guru Stron Jan 14 '22 at 09:04
  • 1
    As I mentioned in my 1st comment, I'm using mailkit's `SmtpClient` – variable Jan 14 '22 at 09:18
  • @variable sorry, missed that! – Guru Stron Jan 14 '22 at 09:19
  • @variable, if this `SmtpClient` you mentioned provides an open channel to mail server and send multiples mails through it at anytime requested, then you can use it as a singleton. on the other hand, if it is one-time use disposable class, then you use it as a static method or wrap it in your own class that can behave as a singleton. – Yılmaz Durmaz Jan 16 '22 at 09:42
2

As you said singleton can be instantiated once, so it's a good place to keep object alive, object you can use during application lifetime. For mvc project, singleton objects are the same for every request.

In your method 2, your SmtpClient doesn't need to create a new instance and dispose it every time.

From msdn doc :

The SmtpClient class implementation pools SMTP connections so that it can avoid the overhead of re-establishing a connection for every message to the same server. An application may re-use the same SmtpClient object to send many different emails to the same SMTP server and to many different SMTP servers. As a result, there is no way to determine when an application is finished using the SmtpClient object and it should be cleaned up.

So it's a good candidate for singleton utility service.

The singleton pattern will look like this :

public class SmtpUtilityService : ISmtpUtilityService, IDisposable
{
    private readonly SmtpClient _smtpClient;
    
    public SmtpUtilityService()
    {
        _smtpClient = new SmtpClient([...]);
    }
    
    public async Task SendEmail(str eaddress)
    {
        await _smtpClient.SendAsync([...]);
    }

    public void Dispose()
    {
        if(_smtpClient != null)
        {
            _smtpClient.Dispose();
        }
    }
}

In your Statup.cs add SmtpUtilityService as singleton to the IServiceCollection and your SmtpClient will be instantiated only once.

By the way microsoft doesn't recommend the use of SmtpClient (obsolete on some platforms and not recommended on others) so not sure if it's a good candidate :/

msdn smtpclient obsolete

And for your first method ConvertMeterToMiles(int mtr), it's just tranformation, one calcul at one time. it doesn't need any properties and it doesn't need an instance. So full static class is good choice.

public static class MeterHelper
{
    public static decimal ConvertMeterToMiles(int mtr)
    {
        return mtr * 0.0006213712;
    }
}

Personally I don't often use singleton. If I need properties I will use scoped or transient services and if I don't I will use full static class (helpers).

Anor
  • 46
  • 2
  • The SmtpClient can be injected rather than Newing it up? – variable Jan 14 '22 at 19:05
  • I don't know how SmtpClient works. Doesn't seem to have any interface (like ISmtpClient) so not sure, you may need to do the new in the startup when you add the service. – Anor Jan 14 '22 at 19:21
  • What does new in startup mean please? – variable Jan 14 '22 at 19:52
  • It's one service registration method, 2nd example on this link [msdn](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#service-registration-methods) – Anor Jan 15 '22 at 09:44
  • Will it also be auto disposed after use? – variable Jan 15 '22 at 13:22
1

A static class is a singleton. The only difference between creating a singleton as a static field or via dependency injection is how it's accessed, that's all.

But in the context of SmtpClient, it's not thread-safe. In fact, the documentation says:

Note

If there is an email transmission in progress and you call SendAsync or Send again, you will receive an InvalidOperationException.

In other words, you can't send two emails at the same time using the same instance of SmtpClient. So using SmtpClient as any kind of singleton isn't a good idea anyway. You're better off either making it scoped, or don't use DI for it at all and just declare a new one when you need it.

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
1

Let me recap this first: a static class cannot have instances so you use its methods with the class's name and a singleton class can only have 1 instance to be shared by others.

for a static class, you need to include that in your code file. for a singleton class, you will first create an instance then pass it to other methods in their parameter list.

In the context of aspnet, since we will have only 1 instance, we leave its creation and disposal to the framework with services.AddSingleton<ISingleton, Singleton>(); and get it to our controller by public SomeController(ISingleton singleton). when visitors hit this controller endpoint, all of their requests will be different but then handled by this single instance. aspnet will determine which singletons your controller needs, by their interfaces, and inject only those requested.

whether be a server-side global state holder, a database connector, or an email-sender in your case, all activity you implement will go through this singleton instance. you can implement a load balancer into it so requests can be processed without bottlenecks.

on the other hand, for a static class, you will prefer short-lived methods as they will run separately for every request to the controller. distance converter is one such method. it won't require any long processes to do its job, nor it will depend on other expensive resources. however, you may want to cache most frequent calculations and send responses from the cache, then converting this distance converter into a singleton that uses resources for long times would be a better idea.

so in short, depending on the use of resources, you will prefer either short-lived independent methods or long-lived methods with lots of expensive operations.


seeing OP has a confusion about SMTPClient he uses, I wanted to add a few more lines.

you need to ask a question: does this client opens a channel to SMTP server and holds it for long time uses, or it just sends 1 message over it and needs be closed after.

some clients have one-time use core functionality, others build upon this core behavior and add a pool of pre-open single-use connections. the core functional class can be used both as a static, given resources as parameters, or be a singleton if it allows having initialized resources other than the connection itself. both cases will need to open a channel to SMTP server only when they are used, and that will cause delays. lastly, if it has to be closed after use, then the core functionality cannot be used as a singleton, as we need the alive throughout the life of our service.

on the other hand, if the client uses a pool of connections, there is no argue that it will be a singleton and positively affect the user experience. a side note here will be implementing one's own class having this pool of connections if no other current library is available in project's use environment.

Yılmaz Durmaz
  • 2,374
  • 12
  • 26
0

I would recommend using an injected singleton. Functionally, it makes very little difference, but injecting a singleton has big advantages when it comes to testing.

Whilst static methods themselves are easy to test, it becomes very difficult to test the code that uses them independently.

Let's take your SendEmail(str eaddress) example. If we implement this as a static helper method, it will be impossible to unit test the code which uses this method without creating a real SMTPClient. By contrast, if we inject a singleton helper class with an interface, this interface can be mocked when testing the code which calls SendEmail.

DaveF
  • 391
  • 2
  • 9
0

There's not difference of funcionality between Singleton and Static.

In a threaded environment like you describe, the only thing that would be a problem is sharing data. If multiple threads access the same properties, there WILL be concurrency problems.

Now, if you are just using utility methods like Sum(int a, int b) which don't have any state, there wont be any problems.

Now, there's basically no difference in this situation between the two, other than the singleton needing to be injected. Even related to web api, there isn't anything really special.

Except maybe that a singleton class can inherit, and a static class cant. But that's another topic.

Ladrillo
  • 79
  • 1
  • 4
  • static classes cannot hold active data. their methods have to initialize every time you use them. a singleton initializes resources at the beginning and holds them active. that makes a big difference – Yılmaz Durmaz Jan 16 '22 at 10:00
  • @YılmazDurmaz In fact, methods are just stored in memory waiting to be used. They don't need to initialize as there are no instances to start with. That's why they are recommended for utility code. And, what do you mean by Active data? – Ladrillo Jan 21 '22 at 20:34
  • I mean you either hard code into the class/method or pass as parameters everything you need every time you use them for a static class. on the other hand, for singleton, other than hardcoded things, maybe domain names, you have an activation step (initializing) with some parameters such as ports, proxies, then you can "change" some of them during the lifetime, like SMTP server, and finally you only pass ever changing ones as parameter, such as an email adress to send an email and the message. this way, singletons become a living object. – Yılmaz Durmaz Jan 21 '22 at 23:29
  • @YılmazDurmaz You can also do it with static classes. It's doable, but that doesn't mean it's a good idea. I prefer leaving static classes for simple things like global read-only data or, in my case, a ValidationHelper which is just an algorithm to check form validity using data annotations :) I use singletons for everything else! – Ladrillo Jan 24 '22 at 15:44