【问题标题】:Singleton in DependencyInjection依赖注入中的单例
【发布时间】:2015-08-25 06:31:12
【问题描述】:

我正在为依赖注入中的单例概念而苦苦挣扎。我不确定类是否应该以支持单例/每个实例实例化的方式实现,以支持打算用作单例的类,或者它们是否应该依赖程序员的正确实例化设置。

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

...
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 类的所有类都将共享一个实例,因此 Settings 将在保存到某个位置时包含所有信息。另一方面,如果程序员没有将类标记为 SingleInstance,则保存时会出现问题,因为如果将其实现为替换存储位置中的整个集合,则不会保存所有设置。因此,正确的功能很大程度上取决于程序员对类的了解并将其用作单例。

在第二个示例中,我将静态字段用于设置,这允许我将类用作单例或作为每个实例的实例而不影响核心功能(我的意思是,如果将使用更多 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>();
    }
}

类使用的两种方法

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

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

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

我的问题:

  • 谁负责定义在依赖容器配置中应将哪个类用作单例?
  • 这些关于实例化的信息应该存储在哪里?
  • 我是否应该实现打算用作单例的类,以便即使在每个实例都实例化的环境中也能工作?
  • 如果我将第 3 方 DLL 的类添加到我的依赖容器配置中,谁负责告诉我应该如何实例化这个类? (程序员如何知道他应该使用哪种实例?这些信息应该是文档的强制性部分,还是程序员应该只使用“尝试/使用”方法?)
  • 是否应该将“单例”仅用作如何使系统更快的方式,但应该是单例以便即使它们被多次实例化也能够工作? (依赖容器配置中缺少 SingleInstance 关键字)

【问题讨论】:

    标签: .net design-patterns dependency-injection


    【解决方案1】:

    当我们需要跨容器共享单个实例时,我们使用 SingleInstance 生命周期范围。即当你需要单例语义时,你使用 SingleInstance 生命周期。

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

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

    谁负责定义应该使用哪个类 依赖容器配置中的单例?

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

    关于实例化的信息应该存储在哪里?

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

    我应该实现打算用作单例的类吗 即使在实例化的环境中也能够工作 实例?

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

    如果我将第 3 方 DLL 的类添加到我的依赖容器 配置,谁负责告诉我应该如何实例化 这节课? (程序员如何知道应该使用哪种实例 他用?该信息应是文件的强制性部分,或 程序员应该只使用“尝试/使用”的方法吗?)

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

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

    应该是“单例”,仅用作使系统更快的方式,但 应该是单例,即使它们被实例化也能够工作 多次? (在依赖容器配置 SingleInstance 关键字丢失)

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-21
      • 1970-01-01
      • 1970-01-01
      • 2017-11-11
      • 2015-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多