C#静态构造函数设计问题 – 需要指定参数

我有一个重新出现的设计问题,某些类需要一次性初始化,参数如外部资源的名称,如配置文件。

例如,我有一个corelib项目,它提供应用程序范围的日志记录,配置和一般帮助方法。 这个对象可以使用静态构造函数来初始化自己,但它需要访问一个它找不到的配置文件。

我可以看到几个解决方案,但这两个似乎都不对:

1)使用带参数的构造函数。 但是,每个需要corelibfunction的对象也应该知道配置文件的名称,因此必须在应用程序周围传递。 另外,如果我将corelib实现为单例,我还必须将配置文件作为参数传递给GetInstance方法,我认为这也是不对的。

2)创建静态属性或方法以传递配置文件或其他外部参数。

我有点使用后一种方法并创建了一个Load方法,它初始化一个内部类,它通过构造函数中的配置文件。 然后通过公共属性MyCoreLib公开这个内部类。

public static class CoreLib { private static MyCoreLib myCoreLib; public static void Load(string configFile) { myCoreLib = new MyCoreLib(configFile); } public static MyCoreLib MyCoreLib { get { return myCoreLib; } } public class MyCoreLib { private string configFile; public MyCoreLib(string configFile) { this.configFile = configFile; } public void DoSomething() { } } } 

我虽然还不开心。 在调用load方法之前,不会初始化内部类,因此需要在访问MyCoreLib的任何位置进行考虑。 也没有什么可以阻止某人再次调用load方法。

任何其他模式或想法如何实现这一目标?

您需要一个共同的位置来存储它。 您可以使用app.config,即使这是一个单独的程序集,也可以通过在库程序集中定义一个config部分并引用它来执行app.config。 或者你可以只为appSettings添加一个通用设置,并在不使用强类型设置的情况下引用它。 如果该值是用户输入的,那么您可以使用隔离存储。 最后,您可以在安装时将其放在注册表中的一个众所周知的位置。

对于代码,以下内容更好地封装

  public interface ICoreLib { void SomeMethod(); } public static class CoreLibManager { private static ICoreLib coreLib; private static volatile bool initialized; private static readonly object lockObject = new object(); public static ICoreLib CoreLib { get { Inititialize(); return coreLib; } } ///  /// The inititialize. ///  private static void Inititialize() { if (initialized) { lock (lockObject) { if (!initialized) { string configFile = // Fech from common location coreLib = new MyCoreLib(configFile); initialized = true; } } } } ///  /// The my core lib. ///  private class MyCoreLib : ICoreLib { public MyCoreLib(string configPath) { } public void SomeMethod() { } } } 

当你有这样的全局状态需要初始化并且它需要外部输入来完成初始化(例如配置文件)时,你就会遇到知道输入必须调用Load或Initialize来初始化的外部代码你的全球状态。 没有办法解决这个问题。

然而,您正确观察到的问题是任何人都可以在正确初始化之前尝试使用全局状态,这是以这种方式暴露它的缺点。 解决这个问题的方法是将全局库的所有有状态部分重构为实例类,并通过应用程序将引用传递给该实例。 因为您可以控制何时创建和初始化它,所以现在可以确保它在传递之前具有有效状态。 您可以利用全球状态的便利性来获得更好的隔热效果。

您可以使用.net配置系统来执行此操作。 最简单的方法是使用web.config文件中的元素,或appname.exe.config文件中的元素。 使用:

 ConfigurationManager.AppSettings["property_name"] 

访问一个属性。 无论您的代码是在Web上下文中运行还是作为Windows应用程序运行,配置系统都会找到配置文件并加载您需要的值。

你也可以使用类型安全的配置值和分层数据构建一个更复杂的系统,但我的需求非常简单,所以我没有在第一次头痛设置后探讨这个问题:)

您可以使该类成为单例,从而确保只有一个实例,同时还允许构造函数参数。

没有太多选择。

要么你传递的东西(在这种情况下,我不会传递字符串,但创建一个具体的(非静态)CoreLib并传递它或你做你的建议。

不要忘记隐藏MyCoreLib的构造函数。 目前它是公开的,可能是无意的。

好的,谢谢大家的帮助。 我重构了CoreLib项目,并将配置处理分解为一个单独的项目。 现在我们有一个解决方案范围的配置管理共享类。 该类可以通过用户设置来处理自己,该用户设置通过静态属性ConfigFile公开。 如果用户通过某个配置对话框更改,则此属性还会保留修改后的文件位置。 如果配置文件发生变化,初始化标志也将被重置。

 public interface IConfig { void SomeMethod(); } public static class ConfigurationManager { private static IConfig config; private static volatile bool initialized; private static readonly object lockObject = new object(); public static string ConfigFile { get { return Properties.Settings.Default.ConfigFile; } set { if (Properties.Settings.Default.ConfigFile == value) return; lock (lockObject) { Properties.Settings.Default.Save(); initialized = false; } } } public static IConfig Config { get { Inititialize(); return config; } } private static void Inititialize() { lock (lockObject) { if (initialized) return; config = new Configuration(Properties.Settings.Default.ConfigFile); initialized = true; } } } internal class Configuration : IConfig { public ClientConfig(string configFile) { // Parse & validate config file } public void SomeMethod() { } } 

所以现在在启动时我们首先validation持久化的ConfigFile设置,然后尝试通过管理器的Config属性访问Configuration实例。 可以在此处理任何解析exception并相应地处理。 然后由开发人员来处理IConfig方法的任何exception。

 if (!System.IO.File.Exists(ConfigurationManager.ConfigFile)) { // Display config file locator dialog ConfigurationManager.ConfigFile = someDialog.FileName; } try { IConfig config = ConfigurationManager.Config; } catch { // Error parsing config file }