【问题标题】:What's wrong with this singleton I created我创建的这个单例有什么问题
【发布时间】:2010-09-01 14:35:36
【问题描述】:

我创建了一个类,它允许访问对变量的全局访问,同时只创建一次,本质上是一个单例。

但是,它与实现单例的任何“正确”方法都不匹配。我认为它没有被提及,因为它有一些“错误”,但除了缺乏延迟初始化之外,我看不出它有任何问题。

有什么想法吗?

static class DefaultFields
{
    private static readonly string IniPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "defaultFields.ini");
    private static readonly IniConfigSource Ini = GetIni();               

    /// <summary>
    /// Creates a reference to the ini file on startup
    /// </summary>
    private static IniConfigSource GetIni()
    {
        // Create Ini File if it does not exist
        if (!File.Exists(IniPath))
        {
            using (FileStream stream = new FileStream(IniPath, FileMode.CreateNew))
            {
                var iniConfig = new IniConfigSource(stream);
                iniConfig.AddConfig("default");
                iniConfig.Save(IniPath);
            }
        }

        var source = new IniConfigSource(IniPath);
        return source;
    }

    public static IConfig Get()
    {
        return Ini.Configs["default"];
    }

    public static void Remove(string key)
    {
        Get().Remove(key);
        Ini.Save();
    }

    public static void Set(string key, string value)
    {
        Get().Set(key, value ?? "");
        Ini.Save();
    }
}

【问题讨论】:

标签: c# design-patterns singleton


【解决方案1】:

它不遵循通常的单例模式,因为你的类是静态的,只是控制对静态变量的访问。

单例通常是一个类的静态单个实例,其中唯一的静态函数是创建和访问将变量存储为普通非静态成员变量的单例。

意思是这个类可以很容易地被改变或被实例化不止一次,但你的却不能

【讨论】:

  • 我试图避免在几个不同的地方声明'IniConfigSource',所以我只是在一个类中声明它,而不是那样。能够只使用 Default.Get() 等,而不必一次又一次地定义这些变量是我的目标。我了解静态类和此类如何正常工作,我只是对静态变量的工作方式感到困惑,只是认为我所做的一定是一种单例。因为它不是一个单例,而且我没有任何特别的愿望要让它成为一个,我想这很好,除了像 'rwmnau' 建议的那样添加一些锁定。
【解决方案2】:

你对单例的看法是对的,它是一个具有提供全局访问的唯一实例的类。

它可能看起来像一个静态类,但通常以不同的方式实现。

还请记住,使用这种模式时应该采取一些预防措施,因为一旦它深入到代码中,就很难重构出单例。应该主要在您有硬件限制或实施了对工厂的唯一访问点时使用。我会尽量避免它。

一个实现的例子如下:

public class A
{
    /// <summary>
    /// Unique instance to access to object A
    /// </summary>
    public static readonly A Singleton = new A();

    /// <summary>
    /// private constructor so it can only be created internally.
    /// </summary>
    private A()
    {
    }

    /// <summary>
    /// Instance method B does B..
    /// </summary>
    public void B()
    {
    }
}

并且可以像

一样使用

A.Singleton.B()

希望对您有所帮助。

【讨论】:

    【解决方案3】:

    类中的所有方法都是静态的,因此您对用户隐藏了单个实例。使用singleton pattern,单个实例通过通常称为Instance 的公共属性公开(在Java 等其他语言中,它可能是称为getInstance 或类似的方法)。

    您的代码没有错 - 它只是不是单例模式。如果你想实现一个单例,我推荐 Jon Skeet 的文章Implementing the Singleton Pattern in C#

    【讨论】:

      【解决方案4】:

      我看到的最大问题是您没有在写入 INI 文件时执行任何SyncLock-ing - 尝试同时写入值的多个线程最终可能会导致不可预知的结果,例如同时写入并且只有一个持久化(或多个线程尝试一次写入文件,导致 IO 错误)。

      我会创建某种类型的私有“锁定”对象,然后将写入文件的内容包装在 SyncLock 中,以确保一次只有一个线程能够更改值(或者,在至少,提交对 INI 文件的更改)。

      【讨论】:

      • 我同意,缺少锁定可能是个问题。 IConfig 实现可能会在其 Remove 和 Set 方法中执行锁定,但由于此代码不知道该实现是什么,我会说 DefaultFields 类应该自己执行锁定。
      【解决方案5】:

      我也对这个问题的答案感兴趣。在我看来,有大量使用惰性实例化的单例示例,但我认为您必须根据具体情况问自己是否真的有必要。

      虽然本文与 Java 有关,但这些概念仍然适用。这为不同的单例实现提供了许多示例。 http://www.shaunabram.com/singleton-implementations/

      我还看到很多对“Effective Java”一书的引用,第 71 条 - 明智地使用惰性实例化。基本上,除非你需要,否则不要这样做。

      【讨论】:

        【解决方案6】:

        这不是真正的单例,它是一个静态类。

        在很多方面,静态类与单例类相似,没错。但是静态类不能实现接口,不能从基类继承功能,也不能携带对它们的引用。

        【讨论】:

          【解决方案7】:

          为什么 Ini 字段是只读的?

          但是如果你想实现单例模式,它是这样的:

          static DefaultFields
          {
              private readonly string IniPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "defaultFields.ini");
              private readonly IniConfigSource Ini = GetIni();               
          
              private static DefaultFields _default;
          
              public static DefaultFields Default 
              { 
                  get { if(this._default == null){ this._default = new DefaultFields(); } return this._default; } 
              }
          
              private DefaultFields()
              {
          
              }
          
              /// <summary>
              /// Creates a reference to the ini file on startup
              /// </summary>
              private IniConfigSource GetIni()
              {
                  // Create Ini File if it does not exist
                  if (!File.Exists(IniPath))
                  {
                      using (FileStream stream = new FileStream(IniPath, FileMode.CreateNew))
                      {
                          var iniConfig = new IniConfigSource(stream);
                          iniConfig.AddConfig("default");
                          iniConfig.Save(IniPath);
                      }
                  }
          
                  var source = new IniConfigSource(IniPath);
                  return source;
              }
          
              public IConfig Get()
              {
                  return Ini.Configs["default"];
              }
          
              public void Remove(string key)
              {
                  Get().Remove(key);
                  Ini.Save();
              }
          
              public void Set(string key, string value)
              {
                  Get().Set(key, value ?? "");
                  Ini.Save();
              }
          }
          

          【讨论】:

            【解决方案8】:

            延迟初始化对于单例类非常重要。通过声明你的类是静态的,你实现了一个静态类,而不是一个单例类。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-09-05
              • 2011-01-17
              • 1970-01-01
              • 1970-01-01
              • 2010-09-14
              相关资源
              最近更新 更多