一个静态成员变量能够被用于在静态类中缓存一个值吗?

9
我看到了一段代码,其中原开发人员似乎尝试使用静态字符串来缓存静态类中的值。
public static class GetStringFromSomeProcess
{
    private static string theAnswer;

    public static string GetString
    {
        get
        {
            if(theAnswer == null)
            {
                theAnswer = GoGetTheAnswerFromALongRunningProcess();
            }
            return theAnswer;
        }
    }
}   

据我所见,这样做不起作用,因为您无法实例化GetStringFromSomeProcess类,每次使用GetString时都会调用GoGetTheAnswerFromALongRunningProcess。我有什么遗漏的吗?

为什么需要实例化 GetStringFromSomeProcess?在第一次调用 GetString 后,theAnswer 将不再为空,因此具有可怕名称的方法将不再被调用。 - Mong Zhu
3个回答

8
这将很好地工作——因为它是静态的,所以只有一个theAnswer实例,并且(也因为它是静态的)可以从公共静态属性中访问。这意味着对其进行的任何更改都将对访问它的所有代码可见。因此,第一次调用GetString将设置theAnswer为非空,并且后续调用将不会调用GetStringFromSomeProcess()
然而,您发布的解决方案不是线程安全的,因为GoGetTheAnswerFromALongRunningProcess()可能会被多个线程同时调用。
.NET提供了Lazy类来解决此问题,如下所示:
public static class GetStringFromSomeProcess
{
    private static readonly Lazy<string> _theAnswer = new Lazy<string>(GoGetTheAnswerFromALongRunningProcess);

    public static string GetString
    {
        get
        {
            return _theAnswer.Value;
        }
    }

    public static string GoGetTheAnswerFromALongRunningProcess()
    {
        return "X";
    }
}

你需要向 Lazy<T> 类的构造函数提供一个方法,以便在需要时调用该方法来创建其封装的对象。在上面的示例中,我将 GoGetTheAnswerFromALongRunningProcess 传递给它的构造函数。
此外,请注意,拥有一个需要很长时间才能返回的属性通常是一个不好的主意。最好将其作为一个方法来实现:
public static string GetString()
{
    return _theAnswer.Value;
}

这个答案中有很多有用的东西,我仍在消化 - 谢谢。我不明白的是,在调用 GetStringFromSomeProcess.GetString 后,为什么 GetStringFromSomeProcess 不会超出范围,而 'cached' 变量也不会丢失。我们说一旦使用了 GetString,它就会在整个应用程序的生命周期中保留下来,这不是我预期的。 - jonnarosey
@jonaglon 当在您的原始代码中调用 GetString 时,它首先检查 theAnswer 是否为 null,仅当它为 null 时才调用 GoGetTheAnswerFromALongRunningProcess() 并将(可能非空)结果分配给 theAnswer。第一次之后,theAnswer 将不再为 null,因此不会再调用 GoGetTheAnswerFromALongRunningProcess() - Matthew Watson

4

你说的没错,这个类不能被实例化,但是它会在应用程序中存在。

因此,只有第一次访问属性时,才会调用方法GetStringFromSomeProcess。之后每次访问时,== null的检查都将解析为false,并返回第一次调用时评估的值。


那么静态类只会在第一次使用时被实例化,并且会一直存在于应用程序的整个生命周期中?我认为我之前的假设是因为它是静态的,所以在使用后不再处于作用域内,可能会被垃圾回收。 - jonnarosey
1
是的,没错。它会在应用程序运行期间一直存在。它永远不会被垃圾回收,因为它始终可用。希望这可以帮到你! - Luke

0

在不创建GetStringFromSomeProces类的对象的情况下,它是否能正常工作?由于字符串theAnswer也是静态的,因此它有可能工作,但我想知道何时初始化该变量。通常,您会像您建议的那样编写代码,并初始化GetStringFromSomeProcess类。

Main.cs

...
GetStringFromSomeProcess getString = new GetStringFromSomeProcess();
string answer = getString.theAnswer();
...

GetStringFromSomeProcess.cs

public class GetStringFromSomeProcess
{
    private string _theAnswer;

    public string theAnswer
    {
        get
        {
            if(theAnswer == null)
            {
                GoGetTheAnswerFromALongRunningProcess getAnswer = new GoGetTheAnswerFromALongRunningProcess();
                _theAnswer = getAnswer.GetAnswer();
            }
            return _theAnswer;
        }
    }
}

因为GetStringFromSomeProcess是静态的,所以new GetStringFromSomeProcess()不是有效的代码。 - jonnarosey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接