Issue setting up Singleton

In my project, I have a Singleton which I want to use in certain files. When I try to implement it in one I get an error. First, let me show some code: The singleton:
public sealed class RegularPuzzleSingleton : ISingleton
{
private const int TrimLength = 13;
private Dictionary<string, Type> _regularTypes;
public IRegularPuzzle RegularPuzzle { get; private set; }

private RegularPuzzleSingleton()
{
GetRegularTypes();
}

public static RegularPuzzleSingleton _instance;

public static RegularPuzzleSingleton GetInstance()
{
_instance ??= new RegularPuzzleSingleton();

return _instance;
}

public void GetRegularTypes()
{
var interfaceType = typeof(IRegularPuzzle);
var implementingTypes = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(type => interfaceType.IsAssignableFrom(type)
&& !type.IsAbstract
&& !type.IsInterface);

foreach (var type in implementingTypes)
{
string name = type.Name.ToLower()[..(type.Name.Length - TrimLength)];
_regularTypes[name] = type;
}
}

public bool IsRegularType(string extension)
{
if (_regularTypes.TryGetValue(extension, out Type? regular) && regular != null)
{
RegularPuzzle = (IRegularPuzzle)Activator.CreateInstance(regular);
return true;
}

return false;
}
}
public sealed class RegularPuzzleSingleton : ISingleton
{
private const int TrimLength = 13;
private Dictionary<string, Type> _regularTypes;
public IRegularPuzzle RegularPuzzle { get; private set; }

private RegularPuzzleSingleton()
{
GetRegularTypes();
}

public static RegularPuzzleSingleton _instance;

public static RegularPuzzleSingleton GetInstance()
{
_instance ??= new RegularPuzzleSingleton();

return _instance;
}

public void GetRegularTypes()
{
var interfaceType = typeof(IRegularPuzzle);
var implementingTypes = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(type => interfaceType.IsAssignableFrom(type)
&& !type.IsAbstract
&& !type.IsInterface);

foreach (var type in implementingTypes)
{
string name = type.Name.ToLower()[..(type.Name.Length - TrimLength)];
_regularTypes[name] = type;
}
}

public bool IsRegularType(string extension)
{
if (_regularTypes.TryGetValue(extension, out Type? regular) && regular != null)
{
RegularPuzzle = (IRegularPuzzle)Activator.CreateInstance(regular);
return true;
}

return false;
}
}
13 Replies
electronic heartbreak.
electronic heartbreak.OP•2y ago
Startup:
private IServiceProvider ServiceProvider { get; set; }

public Startup()
{
InitServiceCollection();

fileReader = new FileReader.FileReader(ServiceProvider.GetService<ISingleton>());
baseSudokuFactory = new();
}

private void InitServiceCollection()
{
var services = new ServiceCollection();
services.AddSingleton<ISingleton, RegularPuzzleSingleton>();
services.AddTransient<IDisplay, Display>();

ServiceProvider = services.BuildServiceProvider();
}
private IServiceProvider ServiceProvider { get; set; }

public Startup()
{
InitServiceCollection();

fileReader = new FileReader.FileReader(ServiceProvider.GetService<ISingleton>());
baseSudokuFactory = new();
}

private void InitServiceCollection()
{
var services = new ServiceCollection();
services.AddSingleton<ISingleton, RegularPuzzleSingleton>();
services.AddTransient<IDisplay, Display>();

ServiceProvider = services.BuildServiceProvider();
}
FileReader:
private readonly ISingleton _singleton;

public FileReader(ISingleton singleton)
{
_singleton = singleton;
}
private readonly ISingleton _singleton;

public FileReader(ISingleton singleton)
{
_singleton = singleton;
}
When i try to run this;
System.InvalidOperationException: 'A suitable constructor for type 'DPAT.LOGIC.FileReader.Extensions.RegularPuzzleSingleton' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.'
System.InvalidOperationException: 'A suitable constructor for type 'DPAT.LOGIC.FileReader.Extensions.RegularPuzzleSingleton' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.'
at line: fileReader = new FileReader.FileReader(ServiceProvider.GetService<ISingleton>()); The thing is, is that a singleton normally wont need a public ctor.
cap5lut
cap5lut•2y ago
and services are registered for all parameters of a public constructor
it needs a public constructor, if it has no constructor at all it implicitly has a public constructor added by the compiler
electronic heartbreak.
electronic heartbreak.OP•2y ago
Yeah i added that one and that works. It still feels like I break something. By not having that private constructor. (I would also think that the getInstance is not needed anymore)
cap5lut
cap5lut•2y ago
well if u really want to force the private constructor u would have to register the singleton using a factory method: services.AddSingleton<ISingleton, RegularPuzzleSingleton>(serviceProvider => RegularPuzzleSingleton.GetInstance());
electronic heartbreak.
electronic heartbreak.OP•2y ago
Okay, but wont the AddSingleton do the Singleton part for me already?
cap5lut
cap5lut•2y ago
yep usually its bad practice to design a class so that it is enforced to be a singleton let the consumer of the class decide how many instances they want ;p
electronic heartbreak.
electronic heartbreak.OP•2y ago
😩 I currently live in a design pattern hell. We need to make a sudoku game with building design patterns I basically wanted a singleton so i can create the class above just once and use it everywere (only 2 places)
cap5lut
cap5lut•2y ago
registering the class as singleton in the DI container is totally fine. u simply decided then that u only need that one "global" instance but because u enforce it via private constructor + static getter method, u are forced to and couldnt for example use an instance per scope
electronic heartbreak.
electronic heartbreak.OP•2y ago
So, leaving the private ctor and getInstance out it is fine?
cap5lut
cap5lut•2y ago
yeah i would get rid of the private constructor and GetInstance and keep registering it as services.AddSingleton<ISingleton, RegularPuzzleSingleton>();
electronic heartbreak.
electronic heartbreak.OP•2y ago
Thank you so much!!
cap5lut
cap5lut•2y ago
glad i could help 😄 i would also not call the interface ISingleton, give it a name that describes what that service is
electronic heartbreak.
electronic heartbreak.OP•2y ago
True, I just named that for now and when I discussed it with my duo we gonna refactor it.
Want results from more Discord servers?
Add your server