The way I see it, there are three reasons for using an Inversion of Control (IoC) / Dependency Injection (DI) container:
To decouple the actual implementation from a base class/interface (the contract), so that we can change the implementation when needed
To allow the implementation itself to have services injected into it when it is built
To control the lifetime of a service, for example, singleton, web request scoped, transient, etc
For #1, one common approach is to create an interface for every service that we are registering in the IoC/DI – this is actually why we call it Inversion of Control: we don’t control it, the container does. Nothing against it, but it may not be necessary: if for the service we are registering there will never, ever, be a different implementation (and we know that this may happen, like, for infrastructure code), there isn’t really a need for registering a service under an umbrella interface (or base class). Of course, this may bite us later, so we need to be really careful about it. The way I sometimes avoid interfaces is because I really don’t need them, and I end up saving some extra code and files.
The second reason is straightforward: we declare, on the implementation’s constructor, the services that we will be taking as dependencies, so that we can keep a reference to them for later usage.
Finally, reason #3, for me, its, for example, what makes Singleton an anti-pattern. Using an IoC container we can control the lifetime and have a service iinjected into our classes, transparently, without caring if there is one or more than one instances of it.