如何延迟属性中的静态初始化

我创建了一个类,它是一个单例(第五个版本)和一个(dependency injection)工厂之间的交叉。 称之为“单声道工厂”? 它工作,看起来像这样:

public static class Context { public static BaseLogger LogObject = null; public static BaseLogger Log { get { return LogFactory.instance; } } class LogFactory { static LogFactory() { } internal static readonly BaseLogger instance = LogObject ?? new BaseLogger(null, null, null); } } //USAGE EXAMPLE: //Optional initialization, done once when the application launches... Context.LogObject = new ConLogger(); //Example invocation used throughout the rest of code... Context.Log.Write("hello", LogSeverity.Information); 

我们的想法是可以扩展单工厂以处理多个项目(例如,不仅仅是记录器)。 但我本来希望单工厂看起来像这样:

 public static class Context { private static BaseLogger LogObject = null; public static BaseLogger Log { get { return LogFactory.instance; } set { LogObject = value; } } class LogFactory { static LogFactory() { } internal static readonly BaseLogger instance = LogObject ?? new BaseLogger(null, null, null); } } 

上面的方法不起作用,因为当触摸Log属性时(通过setter调用)它会导致与getter相关的代码路径被执行…这意味着内部LogFactory“instance”数据总是设置为BaseLogger (设置“LogObject”总是太晚了!)。

那么我可以使用的装饰或其他技巧会导致Log属性的“get”路径在调用设置路径时变得懒惰吗?

注意:这是对原始答案的完全重写; 然而,该建议仍然有效。

首先:确保您没有在调试器下运行。 例如,监视窗口可能会触及您的公共静态属性。 这是第二个例子与第一个例子表现不同的可能原因之一。 这可能听起来很傻,但你永远不会知道。

在.NET 4下,你的第二个例子确实有用,我老实说它也可以在.NET 2下工作。 只要您不小心触摸Context.Log属性或LogFactory.instance字段。 然而,它看起来非常脆弱。

另外,严格来说,你在这里尝试使用的beforefieldinit细微之处可能会让你陷入multithreading应用程序: beforefieldinit的init不需要在与Context.Log[Object]的setter相同的线程上运行。 这意味着当初始化LogFactory.instance时,在线程上还不需要设置Context.LogObject ,而它在另一个上(这样的同步可能会延迟发生)。 所以它不是线程安全的。 您可以尝试通过使Context.LogObject变为volatile来修复此问题,这样就可以立即在所有线程上看到该集合。 但是谁知道我们接下来会遇到的其他竞争条件。

在所有技巧之后,你仍然留下以下相当不直观的结果:

 Context.Log = value1; // OK Context.Log = value2; // IGNORED 

你期望第二次调用setter工作( Context.Log == value2 )或抛出。 不要被默默地忽略。

你也可以去

 public static class Context { private static BaseLogger LogObject; public static BaseLogger Log { get { return LogObject ?? LogFactory.instance; } set { LogObject = value; } } private class LogFactory { static LogFactory() {} internal static readonly BaseLogger instance = new BaseLogger(null, null, null); } } 

这里的结果是有保证的,而且是懒惰的(符合Jon Skeet的第五个单例方法)。 它看起来更清洁恕我直言。

一些提示:

查看generics

我避免使用静态初始化。 这可能会在实践中引起奇怪的问题。 例如,如果您正在构建的内容会抛出错误,那么Windows加载器会告诉您存在问题,但不会告诉您什么。 您的代码实际上从未被调用,因此您没有机会处理该问题的exception。 我在第一次使用时构造了第一个实例。 这是一个例子:

  private static OrderCompletion instance; ///  /// Get the single instance of the object ///  public static OrderCompletion Instance { get { lock (typeof(OrderCompletion)) { if (instance == null) instance = new OrderCompletion(); } return instance; } }