Skip to content

The Singleton<T> class should be removed #3134

@Sergio0694

Description

@Sergio0694

This issue relates to the Singleton<T> type defined in Microsoft.Toolkit.Helpers.Singleton.cs.

I'm wondering whether having this type in the toolkit at all is a good idea, considering it can be fully replaced by only using built-in C# language features, which also result in a faster execution.

The current implementation of the Singleton<T> class is as follows:

public static class Singleton<T>
    where T : new()
{
    // Use ConcurrentDictionary for thread safety.
    private static ConcurrentDictionary<Type, T> _instances = new ConcurrentDictionary<Type, T>();

    /// <summary>
    /// Gets the instance of the Singleton class.
    /// </summary>
    public static T Instance
    {
        get
        {
            // Safely creates the first instance or retrieves the existing instance across threads.
            return _instances.GetOrAdd(typeof(T), (t) => new T());
        }
    }
}

This works, but there are a few issues with this implementation:

  • That _instances field is within a generic type! This means that for each Singleton<T> type, that field will be different, and an entirely new ConcurrentDictionary<Type, T> instance will be built. Furthermore, since each T instance is only created once, it means that that dictionary will never really be used at all. When the Instance property is accessed, _instances will always either be empty, or with a single object of type T, since that's the one being looked for in the current Singleton<T> type. Might as well just use a statically initialized field.
  • The thread-safety here is guaranteed by that _instance being statically initialized, since the static class constructor is run under a lock (see here and here). But this means that the same thread-safe behavior could be achieved without the need of a Singleton<T> class in the first place.

Consider this snippet:

public class MyClass
{
    public static MyClass Instance { get; } = new MyClass();
}

This is translated to (see here):

public class MyClass
{
    [CompilerGenerated]
    private static readonly MyClass <Instance>k__BackingField = new MyClass();

    public static MyClass Instance
    {
        [CompilerGenerated]
        get
        {
            return <Instance>k__BackingField;
        }
    }
}

Which again, causes the C# compiler to emit this code in the static constructor for MyClass:

.method private hidebysig specialname rtspecialname static 
    void .cctor () cil managed 
{
    newobj instance void MyClass::.ctor()
    stsfld class MyClass MyClass::'<Instance>k__BackingField'
    ret
}

And this static constructor (see linked issues above) is run under a lock in a given app-domain and only executed once, This is all that's necessary to ensure that a given T instance is only created once when the static T Instance property is accessed the first time (since doing so triggers the static constructor).

Suggestions:

  • Remove the Singleton<T> class entirely from the toolkit.
  • Provide docs to inform developers of this built-in technique to implement a T Instance field for the singleton pattern, without the need of additional APIs at all.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions