如何在属性中延迟静态初始化 [英] How to delay static initialization within a property

查看:156
本文介绍了如何在属性中延迟静态初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我做了一个类,它是一个单(第五版)之间的交叉和(依赖注射)的工厂。称这是一个单工厂?它的工作原理,看起来是这样的:

I've made a class that is a cross between a singleton (fifth version) and a (dependency injectable) factory. Call this a "Mono-Factory?" It works, and looks like this:

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

我们的想法是对的单厂可以扩大,以处理多个项目(如比记录仪以上)。不过,我本来希望已经取得了单工厂是这样的:

The idea is for the mono-factory could be expanded to handle more than one item (e.g. more than a logger). But I would have liked to have made the mono-factory look like this:

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

以上是不行的,因为一时的日志属性被感动(由一个二传手调用)它会导致相关的吸气剂的code路径执行......这意味着内部的LogFactory实例数据总是被设置为BaseLogger(设置LogObject永远是太晚了!)。

The above does not work, because the moment the Log property is touched (by a setter invocation) it causes the code path related to the getter to be executed...which means the internal LogFactory "instance" data is always set to the BaseLogger (setting the "LogObject" is always too late!).

那么,有没有一种装饰或其他把戏我可以使用,将导致而设置的路径被调用偷懒日志属性的得的路径?

So is there a decoration or other trick I can use that would cause the "get" path of the Log property to be lazy while the set path is being invoked?

推荐答案

注意:这是原来的答案完全重写;推荐但是,仍然会代表。

Note: This is a complete rewrite of the original answer; the recommendation still stands, however.

首先:确保你不是在一个调试器中运行。例如,监视窗口可以摸摸你的公共静态属性。这是可能的原因,第二实施例可以行为不同于第一个。这可能听起来很傻,但你永远不知道。

First: make sure you're not running under a debugger. For example, a watch window could touch your public static properties. This is one of the possible reasons the second example could behave differently from the first. It may sound silly, but you never know.

在.NET 4,你的第二个例子做的工作,我会诚实的期望的它在.NET 2正常工作。只要你不碰 Context.Log 属性或 LogFactory.instance 现场不经意间。然而,它看起来非常脆弱。

Under .NET 4, your second example does work, and I'd honestly expect it to work under .NET 2 as well. As long as you don't touch the Context.Log property or LogFactory.instance field inadvertently. Yet, it looks terribly fragile.

此外,严格来说, beforefieldinit 奥妙你想使用这里可以咬你在多线程应用程序:的init的LogFactory 不需要在同一个线程的二传手Context.Log [对象] 运行。这意味着,当 LogFactory.instance 被初始化,上的的线程 Context.LogObject 需要不能设置还没有,而它是在另一个(例如同步可以懒洋洋地发生)。所以它的没有的线程安全的。您可以尝试通过解决这个问题 Context.LogObject 挥发性,这样的设定是在一次看到所有线程。但谁知道还有什么其他的比赛条件下,我们进入下一个。

Also, strictly speaking, the beforefieldinit subtleties you're trying to use here can bite you in a multi-threaded application: the init of LogFactory does not need to run on the same thread as the setter of Context.Log[Object]. This means that when LogFactory.instance is initialized, on that thread Context.LogObject need not be set yet, while it is on another (such syncs can happen lazily). So it is not thread safe. You can try to fix this by making Context.LogObject volatile, that way the set is seen on all threads at once. But who knows what other race conditions we get into next.

和之后所有的招数,你仍然留下了以下而直观的结果是:

And after all the tricks, you're still left with the following rather unintuitive result:

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

您所期待的制定者的第二次调用要么工作( Context.Log ==值2 )或抛出。为了不被忽略。

You'd expect the second invocation of the setter to either work (Context.Log == value2) or to throw. Not to be silently ignored.

您也可以去

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

下面的结果的的保证,懒惰(符合乔恩斯基特的第五个单法)。它看起来很多清洁恕我直言。

Here the result is guaranteed, and lazy (in line with Jon Skeet's fifth singleton method). And it looks a lot cleaner IMHO.

这篇关于如何在属性中延迟静态初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆