Is it a good practice to pass instances of classes which are created only once at application start to other classes
Yes! That's called dependency injection (DI), because you inject each object's dependencies as constructor parameters. The application entry point is called the "composition root" in DI parlance:
A Composition Root is a (preferably) unique location in an application where modules are composed together.
Passing objects to other objects via the constructor is called the Constructor Injection Pattern.
over a static container class which only has getter and setter to get the instance or would that be a use case for singletons?
Using singletons is almost certainly worse.
Here's Mark's commentary on a similar question:
You are right that if you use the container as a Service Locator, it's
more or less a glorified static factory. For lots of reasons I
consider this an anti-pattern.
One of the wonderful benefits of Constructor Injection is that it
makes violations of the Single Responsibility Principle glaringly
obvious.
When that happens, it's time to refactor to Facade Services. In short,
create a new, more coarse-grained interface that hides the interaction
between some or all of the fine-grained dependencies you currently
require.
Creating a "real" singleton class is almost certainly worse than instantiating your object, and handing it to the other objects that need it as a constructor parameter.
In your case, I would do something like this:
static void Main()
{
// This is your composition root
var numberProvider = new NumberProvider();
var b = new B(numberProvider);
b.DoSomething();
}
class NumberProvider
{
private int _number = 0;
// In the real world, maybe this gets a value from a web service
// and implements an INumberProvider interface so you can mock it
// for unit testing
public int GetNumber() => _number++;
}
class B
{
private readonly NumberProvider _numberProvider;
public B(NumberProvider numberProvider)
{
_numberProvider = numberProvider;
}
public void DoSomething()
{
Console.WriteLine(_numberProvider.GetNumber());
}
}
Avoid the public setters. And chances are, you don't need everything to be public.