【问题标题】:How to initialise a class at program startup?如何在程序启动时初始化一个类?
【发布时间】:2011-04-24 09:33:50
【问题描述】:
 class Statistics
{
    Dictionary<decimal, Statistic> BabyDataStandardAgeHeadCircumference;
    Dictionary<decimal, Statistic> BabyDataStandardAgeLength;
    Dictionary<decimal, Statistic> BabyDataStandardAgeWeight;
    Dictionary<decimal, Statistic> BabyDataStandardWeightForLength;


}

这些数据是固定的——只读的,我想从数据库中加载它们。 问题是

  1. 如何使它们只读?(我知道 通过属性)
  2. 如何在程序启动时填充数据?

从启动事件中调用 Statistics.load() 之类的方法对我来说似乎很糟糕..

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    问题:

    如何使它们只读?(我知道 通过属性)

    答案:与其使用Dictionary&lt;decimal, Statistics&gt;,不如考虑为字典使用不可变的包装器。

    此处为不可变 Dictionary 类的实现的链接:

    Does C# have a way of giving me an immutable Dictionary?

    问题:

    如何在程序中用数据填充它们 启动?

    答案:与其在程序启动时用数据填充它们,不如在第一次访问类(静态或实例化)时填充它们?这意味着在静态构造函数中填充这些字典(当然字典也必须是静态成员)。

    public class Statistics
    {
        public static readonly ImmutableDictionary<decimal, Statistic> 
            BabyDataStandardAgeHeadCircumference;
        public static readonly ImmutableDictionary<decimal, Statistic> 
            BabyDataStandardAgeLength;
        public static readonly ImmutableDictionary<decimal, Statistic> 
            BabyDataStandardAgeWeight;
        public static readonly ImmutableDictionary<decimal, Statistic> 
            BabyDataStandardWeightForLength;
    
        static Statistics()
        {
            // populate the dictionaries here...
        }
    }
    

    【讨论】:

      【解决方案2】:

      您可以让 Statistics 类遵循Singleton Pattern;如果尚未创建实例,则在 Instance 属性中创建一个实例:

      class Statistics
      {
          private static Statistics sInstance;
          private static Object sInstanceLock = new Object();
          private Statistics() {
              // Load data from database here...
          }
          public static Statistics Instance {
              get {
                  lock(sInstanceLock) {
                      if(sInstance == null) {
                          sInstance = new Statistics();
                      }
                      return sInstance;
                  }
              }
          }
      }
      

      这意味着初始化不一定要在启动时进行,尽管您可以在启动时调用 Instance 属性。

      至于只读方面,我不确定您的要求是什么。如果您的意思是没有人能够更改字典(添加元素,删除元素),您可以在此处查看问题的答案:Immutable Dictionary。如果您的意思是没有人可以更改存储为字典中特定条目的值的对象,那么您必须使存储在那里的类或值类型不可变,.Net 中的 String 类就是一个例子这个。

      【讨论】:

        【解决方案3】:

        实际上,您列出的属性是基于 Dictionary 类的集合。 Dictionary 中对象的管理(Add()、Remove()、[] indexer)可由任何可以看到 BabyData... 对象的实例访问。换句话说,您不能将它们设置为只读,因为 Dictionary 类不支持该操作模式通过 properties。您必须将 Dictionary 声明为 private,然后通过方法连接每一个。这是“肮脏”的做法。

        或者,您可以基于 Dictionary 类创建一个类,该类通过重载控制对象的添加。这更复杂,但更符合您的需求(据我所知)。

        就从数据库加载而言,您需要告诉我们如何从数据库中获取数据(通过 Sql、Linq to Sql、DataSet 表适配器等)以确定最佳方法用于将数据提取到您的 Dictionary 集合中。

        【讨论】:

          【解决方案4】:

          创建一个可以创建、加载和返回您的 Statistics 类的类,我将通过接口公开执行此操作的方法:

          public interface IStatisticsRepository {
              Statistics GetStatistics();
          }
          
          public class StatisticsRepository : IStatisticsRepository {
          
              private Statistics _statistics = null;
          
              private void LoadStatistics() {
                   _statistics = GetFromDBHere();
              }
          
              public Statistics GetStatistics() {
                  if (_statistics == null) {
                      LoadStatistics();
                  }
          
                  return _statistics;
          
          }
          

          那么,您对该接口的实现就是可以在任何地方使用的服务。我会把它注入任何你需要获取统计信息的地方。

          【讨论】:

          • either StatisticsRepository 仅由单个线程使用(或在您需要时创建),或者您的代码不是线程安全的。如果是前者,初始化将比一次更频繁地发生,正如 OP 所要求的那样。
          • OP 中没有要求只从数据库中获取数据一次。如果您称其为“非线程安全”,那么您对线程安全的定义很奇怪。此实例不可能进入无效状态,最糟糕的情况是如果多个线程竞相进入初始化,则它可能会多次检索数据。
          【解决方案5】:

          如果您将它们设为只读使用属性,字典本身仍然是可修改的。一种选择是将它们设为私有并提供 GetXXX 方法来检索值。

          class Statistics
          {
              private static Dictionary<decimal, Statistic> babyDataStandardAgeHeadCircumference;
              private static Dictionary<decimal, Statistic> babyDataStandardAgeLength;
              private static Dictionary<decimal, Statistic> babyDataStandardAgeWeight;
              private static Dictionary<decimal, Statistic> babyDataStandardWeightForLength;
          
              static Statistics() 
              {
                  // Put code to load from database here.  Will get called *once* when 
                  // the *class* is first initialized.
              }
          
              public Statistic GetBabyDataStandardAgeHeadCircumfrence(decimal val) 
              {
                  return babyDataStandardAgeHeadCircumfrence[val];
              }
          
              ...
          }
          

          【讨论】:

          • 将连接到数据库的代码放在静态类构造函数中的想法很糟糕。如果失败,除了终止进程并重新启动它之外,您无法恢复。
          • @qstarin,如果你连接数据库失败,你的问题已经很严重了。您打算如何从断开连接的数据库中“恢复”?
          • +1 你想用 Get 方法包装字典,这样你以后就不会意外地修改它们。你也可以将它们设置为只读并在构造函数中设置它们而不是使它们成为静态的。
          • @Kirk Woll:再试一次。向用户询问不同的连接参数。故障转移到备份数据库。许多不同的方法,我已经投入生产,在某些情况下,这是为了纠正你的例子所产生的问题。尝试一个长时间运行的服务,该服务在第一次命中静态 ctor 时会出现故障。使用重试的实例和操作策略更容易处理。经验之谈。
          • @Kirk Woll:更不用说这如何完全消除了对其进行单元测试的能力。
          【解决方案6】:

          使其成为只读:让自己成为一个以只读方式实现IDictionary 的包装类。

          在应用程序启动时初始化:

          • 如果您确实需要初始化操作的精确顺序,Statistics.Load() 方法并不奇怪。此外,这为您在数据库连接失败时进行错误处理提供了合理的空间。
          • 如果您不必关心初始化顺序,请使用静态构造函数。通过遵循这种方法,您必须确保数据库是可访问的(换句话说,不会出现任何合法的错误情况)并且第一次使用类(实际上触发静态构造函数)不是“太早了”。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-09-20
            • 2021-03-29
            • 2011-11-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多