代码之家  ›  专栏  ›  技术社区  ›  Brent Arias

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

  •  0
  • Brent Arias  · 技术社区  · 15 年前

    我上的课是一门 singleton (fifth version) 以及(依赖注入)工厂。称之为“单一工厂”?它可以工作,看起来像这样:

    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);
    

    其想法是,Mono工厂可以扩展到处理多个项目(例如,多个记录器)。但是我想让Mono工厂看起来像这样:

    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);
        }
    }
    

    上面的操作不起作用,因为当日志属性被(setter调用)接触时,它会导致与getter相关的代码路径被执行…这意味着内部logfactory“instance”数据总是设置为baselogger(设置“logobject”总是太迟!).

    那么,在调用设置的路径时,是否有一个装饰或其他技巧可以使日志属性的“get”路径变慢?

    2 回复  |  直到 15 年前
        1
  •  3
  •   Ruben    15 年前

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

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

    在.NET 4下,您的第二个示例确实有效,而且我会诚实地 期待 它也可以在.NET 2下工作。只要你不碰 Context.Log 财产或 LogFactory.instance 不经意的字段。然而,它看起来非常脆弱。

    而且,严格来说, beforefieldinit 您试图在这里使用的微妙之处可能会在多线程应用程序中咬到您:初始化 LogFactory 不需要与的setter在同一线程上运行 Context.Log[Object] . 这意味着当 logfactory.instance 已初始化,打开 那个 线 Context.LogObject 当它在另一个上时,还不需要设置(这样的同步可能发生得很慢)。就是这样 线程安全。你可以通过 context.logo对象 不稳定的 ,这样就可以在所有线程上同时看到该集。但谁知道我们接下来会遇到什么样的比赛条件呢?

    在所有的技巧之后,你仍然会得到以下相当不具说服力的结果:

    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);
        }
    }
    

    这里的结果 有保证,而且懒惰(符合乔恩·斯基特的第五个单子方法)。而且看起来干净多了。

        2
  •  0
  •   Jay    15 年前

    一些提示:

    退房 Generics

    我避免使用静态初始化。这在实践中会引起一些奇怪的问题。例如,如果您正在构造的内容引发错误,那么Windows加载器将告诉您有问题,但不会告诉您什么。实际上从未调用过您的代码,因此您没有机会让异常处理该问题。我在第一次使用时构造第一个实例。下面是一个例子:

        private static OrderCompletion instance;
    
        /// <summary>
        /// Get the single instance of the object
        /// </summary>
        public static OrderCompletion Instance
        {
            get
            {
                lock (typeof(OrderCompletion))
                {
                    if (instance == null)
                        instance = new OrderCompletion();
                }
                return instance;
            }
        }
    
    推荐文章