依赖注入中的单例

Singleton in DependencyInjection

我正在努力解决依赖注入中的单例概念。我不确定 classes 是否应该以支持旨在用作单例的 classes 的单例/每个实例实例化的方式实现,或者它们是否应该依赖于程序员的正确实例化设置。

如果以下 class 在依赖容器中被标记为单例,它将按预期工作

...
builder.RegisterType<ApplicationSettings>().AsSelf().SingleInstance();
...


/// <summary>
/// This allows to create many ApplicationSettings instances which each of them will have its collection of settings. 
/// Thus we cannot guarantee that one of class has complete settings
/// </summary>
public class ApplicationSettings
{
    private readonly object _locker = new object();
    private readonly Dictionary<string, object> _settings;
    private readonly ILog _log;

    public ApplicationSettings(ILog log)
    {
        _log = log;
        _settings = LoadSettings();
        Thread.Sleep(3000); //inner hardwork, e.g. cashing of something
    }

    public object GetSettings(string key)
    {
        lock (_locker)
        {
            return _settings.ContainsKey(key) ? _settings[key] : null;
        }
    }

    public void SetSettings(string key, object value)
    {
        lock (_locker)
        {
            _settings.Remove(key);
            _settings.Add(key, value);
        }
    }

    public void Remove(string key)
    {
        lock (_locker)
        {
            _settings.Remove(key);
        }
    }

    public void Save()
    {
        Thread.Sleep(5000); //Saving somewhere
    }

    private Dictionary<string, object> LoadSettings()
    {
        Thread.Sleep(5000); //Long loading from somewhere
        return new Dictionary<string, object>();
    }
}

所有需要使用 ApplicationSettings class 的 class 将共享一个实例,因此设置将包含保存到某个地方时的所有信息。另一方面,如果程序员没有将 class 标记为 SingleInstance,则在保存时会出现问题,因为如果将其实现为替换存储位置中的整个集合,则并非所有设置都会得救。因此,正确的功能在很大程度上取决于程序员对 class 并将其用作单例的知识。

在第二个例子中,我在设置中使用静态字段,这允许我将 class 用作单例或每个实例的实例,而不影响核心功能(我的意思是,如果更多实例,则不会保存所有设置将使用 ApplicationSettings2)

/// <summary>
/// This allows to create many ApplicationSettings2 instances which each of them will share same collection of settings. 
/// </summary>
public class ApplicationSettings2
{
    private static readonly object Locker = new object();
    private static readonly Dictionary<string, object> Settings;
    private readonly ILog _log;

    static ApplicationSettings2()
    {
        Settings = LoadSettings();
        Thread.Sleep(3000); //inner hardwork, e.g. cashing of something
    }


    public ApplicationSettings2(ILog log)
    {
        _log = log;
    }

    public object GetSettings(string key)
    {
        lock (Locker)
        {
            return Settings.ContainsKey(key) ? Settings[key] : null;
        }
    }

    public void SetSettings(string key, object value)
    {
        lock (Locker)
        {
            Settings.Remove(key);
            Settings.Add(key, value);
        }
    }

    public void Remove(string key)
    {
        lock (Locker)
        {
            Settings.Remove(key);
        }
    }

    public void Save()
    {
        Thread.Sleep(5000); //Saving somewhere
    }

    private static Dictionary<string, object> LoadSettings()
    {
        Thread.Sleep(5000);
        return new Dictionary<string, object>();
    }
}

class 用法的两种方法

...
builder.RegisterType<ApplicationSettings>().AsSelf().SingleInstance();
...

...
builder.RegisterType<ApplicationSettings>().AsSelf();
...

将导致相同的预期功能。唯一的区别是单例实例化模式不会导致功能变慢(ctor 中有睡眠),但最终改变实例化模式不会破坏任何东西。

我的问题:

当我们需要跨容器共享单个实例时,我们使用 SingleInstance lifetimescope。即,当您需要单例语义时,您可以使用 SingleInstance lifetimescope。

不应该 手动针对静态字段进行编码,以便能够将 class 用作单个实例。因为这违背了使用 "IOC Containter" 来管理对象生命周期的意义。此外,如果您这样做,则不能将对象用作每个依赖生命周期范围的实例,因为您将在两个不相关的实例之间共享数据。

所以你的第一个例子是对的,第二个是错的。

Who is responsible for defining which class should be used as singleton in dependency container configuration?

决定他需要单例语义的开发人员就是决定它的人。

Where should be this information about instancing stored?

不确定你的意思。澄清后会更新我的答案。

Should I implement classes which are intending to be used as singleton to be able to work even in environment where it is instancing per instance?

您不需要做任何特别的事情就可以让它与每个依赖项的实例一起工作。如果 class 被设计为充当单例(即线程安全),那么它可以轻松地处理每个依赖项的实例(使用一些不必要的同步,例如锁)。

If I am adding 3rd party DLL's class to my dependency container configuration, who is responsible to inform me how should I instancing this class? (How programmer can know which kind of instancing should he use? Should be this information mandatory part of documentation, or should programmer just use "try/use" approach?)

您需要阅读所用组件的文档以确保它是线程安全的。如果是,那么您可以将其用作单个实例。否则,您需要在组件上添加一些包装器,以添加适当的同步以使其线程安全。如果没有可用的文档,那么您可能需要咨询作者、阅读源代码(如果可用)或使用反编译器查找实现以确保它是线程安全的。

你几乎可以使用任何 class 作为每个依赖项的实例(可能有一些内存开销)。

Should be "singleton" used only as way how to make system faster but should be singletons to be able to work even if they are instanced many times? (in dependency container configuration SingleInstance keyword is missing)

不确定我是否理解这一点,如果你的意思是说单例实例即使在每个依赖项注册为实例时也应该工作。是的,应该。