【问题标题】:Using a Singleton in C# App在 C# 应用程序中使用单例
【发布时间】:2014-07-16 22:44:15
【问题描述】:

过去几天我一直在阅读并尝试不同的方法来创建和使用我的一个类文件的单例实例,然后在 stackoverflow 上的整个程序生命周期中使用我需要的引用以及其他网站很少,并且发现有太多的意见、建议和示例让我有些困惑,尤其是在实际使用单例对象时。

我有一些功能,但我不认为这是最正确的方法,我可能会遗漏一步。

我的程序中有一个名为 clsRegistry 的简单类文件,它实现为单例。

public sealed class clsRegistry
{
  //http://tech.pro/tutorial/625/csharp-tutorial-singleton-pattern

  private static clsRegistry RegInstance;

  private clsRegistry() { }

  public static clsRegistry GetInstance()
  {
    lock (typeof(clsRegistry))
    {
      if (RegInstance == null)
      {
        RegInstance = new clsRegistry();
      }
      return RegInstance;
    }
  }

  public string dataBase { get; set; }
  public string userId { get; set; }
  public string passWord { get; set; }

  public void ReadKeys()
  {
    //http://stackoverflow.com/questions/1388787/information-in-registry
    RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Software\Key1\Key2\Key3");
    dataBase = key.GetValue("ADODataSource").ToString().Trim();
    userId = key.GetValue("ServerUserID").ToString().Trim();
    passWord = key.GetValue("ServerPassword").ToString().Trim();
  }
}   // end class definition

从 Main 方法调用时正确实现并调用类中的一个公共方法(ReadKeys)

static class Program
{
  /// <summary>
  /// The main entry point for the application.
  /// </summary>
  [STAThread]
  static void Main()
  {
    // create instance of registry class 
    clsRegistry regData = clsRegistry.GetInstance();

    // call method
    regData.ReadKeys();

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    //Application.Run(new frmMain());
    Application.Run(new frmLogin());
  }
}

创建类的单例后,我加载了一个小的登录表单,我希望单例中的数据库名称显示在表单中,这是我的代码不够优雅的地方。

private void frmLogin_Load(object sender, EventArgs e)
{
  // create a 'new' instance to the singleton?
  clsRegistry regData = clsRegistry.GetInstance();

  lblDataBase.Text = regData.dataBase;
}

我真的需要创建另一个对我的单例对象的引用以读取一些值(或稍后调用另一个方法)还是我在某个地方错过了一个步骤?我的操作假设是,一旦我将一个类的实例创建为单例并且它保留在同一个命名空间中,我就可以访问任何公共值或方法。没有?

作为参考,我正在使用 Visual Studio 2010 和 4.0 框架(是的,我是另一个经典的 VB 开发人员,终于跳槽了)

【问题讨论】:

  • 我认为你做得对。仅仅因为您创建了单例对象的实例并不意味着您应该能够在范围之外使用对它的引用——它不是全局变量。如果您想在另一个类中使用该实例,则必须像在这里一样获得对它的引用。您不是在创建一个“新”单例,只是获取对现有单例的引用。至少这是我的理解。
  • 您不是在“创建”一个新实例,只是获取单个实例的引用
  • 如果您最近几天都在阅读它,您一定偶然发现了Jon Skeets suggestion?在我看来,这是一个非常巧妙的实现
  • 请务必阅读this question
  • 你不应该公开ReadKeys,你应该只是在构造函数中初始化数据。您的属性也不应该有二传手。最重要的是,您确定要将用户名和密码存储在注册表中吗,这听起来不安全。

标签: c#


【解决方案1】:

首先,Microsoft 有 guidelines 专门用于在 C# 中实现单例,它非常详细地解释了创建单线程和多线程单例类的最佳方法。

就您的代码而言:

  1. 调用 GetInstance 时,您并未创建对象的另一个实例。代码返回先前创建的实例。如果它为空(仅一次),它只会实例化该类的新实例。

  2. 您应该在 .NET 中将 Instance 设为属性而不是方法(这样更简洁)。

  3. 在编写 .NET 代码时应该坚持 Pascal 大小写,如果您正在编写 Java 代码,我建议您坚持使用 camelCase,因为这是该语言的标准大小写。

【讨论】:

    【解决方案2】:

    首先:在 C# 中,您不需要锁等。只需通过静态初始化创建实例,您就可以使用线程安全了。

    public sealed class ClsRegistry
    {
        private static ClsRegistry RegInstance = new ClsRegistry();
    
        private clsRegistry() { }
    
        public static ClsRegistry Instance {
            get { return RegInstance; }
        }
    
        public string DataBase { get; set; }
        public string UserId { get; set; }
        public string PassWord { get; set; }
    
        public void ReadKeys()
        {
            // ...
        }
    }
    

    话虽如此,您可以通过ClsRegistry.Instance 简单地访问单例及其属性/方法,而无需先将其分配给新变量:

    static void Main()
    {
        ClsRegistry.Instance.ReadKeys();
        // ...
    }
    

    private void frmLogin_Load(object sender, EventArgs e)
    {
        lblDataBase.Text = ClsRegistry.Instance.DataBase;
    }
    

    请注意,我更改了您的类和属性的名称,以便每个第一个字母都是大写字母,这是 C# 中的一种约定

    【讨论】:

    • 虽然这肯定是定义单例的一种更简单的方法,但它绝不会回答所提出的实际问题。
    • @Servy 这可能是真的,但它绝对是 OP 的起点。还是您希望我浏览他发布的所有代码并为他重写?从编写计算机程序的人那里,我可以期待他们自己的一些想法,不是吗?
    • 这是一个不相关的切线。当然,在 cmets 中指出一些合适的东西,就像许多其他人在你发布答案之前所做的那样,但不回答问题就发布答案是不合适的。
    • @GerbenRampaart 如果您想知道实际问题是什么,只需对? 执行简单的 ctrl + F 即可看到问题所在。为您引用它:“我真的需要创建另一个对我的单例对象的引用才能读取一些值 [...]?”他并没有要求关于如何创建单例的最佳实践。他在问如何使用他已经创建的单例。
    • @GerbenRampaart 所以你找不到问题,我告诉你如何找到它,你仍然找不到它,我正在贬低它?对于您引用的部分,您有什么理由相信“什么不起作用”是单例本身?他明确表示他认为他的单身人士有效,他担心他是如何称呼它的。 是他似乎“不相信这是最正确的方式”。这种观点当然得到了他的支持,他说在他提出的实际问题中你似乎没有费心去寻找或阅读。
    【解决方案3】:

    您需要询问单例以获取其引用以访问这些值,是的。如果您不想,则不需要将该引用存储在局部变量中,尽管您当然可以。如果您不想这样做,可以直接从实例中访问该值:

    lblDataBase.Text = clsRegistry.GetInstance().dataBase;
    

    【讨论】:

    • uhm.. downvoter - 请解释一下?我不明白为什么这值得一票否决
    • @Default 我已经解释了为什么发布到这个问题的其他两个答案不是合适的答案,同时这些答案吸引了反对票,这一事实可能与此有关。
    • @Servy 你可以相信我不会对任何人投反对票以报复他们。所以不,我不知道你为什么被否决。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-17
    • 1970-01-01
    • 2014-05-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多