Reputation: 560
I've come across this piece of code where it looks like the original developer has tried to use a static string to cache a value in a static class.
public static class GetStringFromSomeProcess
{
private static string theAnswer;
public static string GetString
{
get
{
if(theAnswer == null)
{
theAnswer = GoGetTheAnswerFromALongRunningProcess();
}
return theAnswer;
}
}
}
As far as I can see this won't work, as you can't instantiate the GetStringFromSomeProcess
class, GoGetTheAnswerFromALongRunningProcess
will be called every time GetString
is used. Am I missing something?
Upvotes: 9
Views: 6628
Reputation: 1
Does it work correctly without creating an object of the GetStringFromSomeProces class? Since the string, theAnswer, is also static, it could potentially work, but I'm wondering when that variable will be initialized. Typically you would code it like you are suggesting with initialization of the GetStringFromSomeProcess Class.
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;
}
}
}
Upvotes: 0
Reputation: 109792
This will work fine - there is only one instance of theAnswer
because it is static - and (also because it is static) it can be accessed from a public static property. This means that any changes made to it will be visible to all code that accesses it. So the first call to GetString
will set theAnswer
to non-null, and subsequent calls will not make a call to GetStringFromSomeProcess()
.
However, the solution you posted is not threadsafe because GoGetTheAnswerFromALongRunningProcess()
could be called simultaneously by multiple threads.
.Net provides the Lazy
class to solve this issue, as follows:
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";
}
}
You supply to the constructor of the Lazy<T>
class a method that it can call when needed in order to create the object that it is wrapping. In the example above, I pass GoGetTheAnswerFromALongRunningProcess
to its constructor.
Also note that it's usually a bad idea to have a property that can take a very long time to return. It's better to make it a method:
public static string GetString()
{
return _theAnswer.Value;
}
Upvotes: 8
Reputation: 23690
You are right in saying that the class can't be instantiated, but the class will exist within the application.
Therefore, only the first time the property is accessed, will the method GetStringFromSomeProcess
be called. Every other time after that, the check for == null
will resolve to false
and the value evaluated by the first call will be returned.
Upvotes: 4