Related: How do I create a static local variable in Java?
Pardon if this is a duplicate; I was pretty sure this would have been
No, C# does not support this. You can come close with:
private static System.Text.RegularExpressions.Regex re =
new System.Text.RegularExpressions.Regex("\\(copy (\\d+)\\)$");
private static string AppendCopyToFileName(string f)
{
}
The only difference here is the visibility of 're'. It is exposed to the classm not just to the method.
The re
variable will be initialized the first time the containing class is used in some way. So keep this in a specialized small class.
C# doesn't support static local variables. In addition to what has been posted above, here's a 2004 MSDN blog entry on the subject: Why doesn't C# support static method variables?
(Same blog entry in the Microsoft's own archive. The Web Archive preserved the comments. Microsoft archive didn't.)
Unfortunately, no. I really loved this possibility in C.
I have an idea what you could do.
Create a class that will provide access to instance-specific values, which will be preserved statically.
Something like this:
class MyStaticInt
{
// Static storage
private static Dictionary <string, int> staticData =
new Dictionary <string, int> ();
private string InstanceId
{
get
{
StackTrace st = new StackTrace ();
StackFrame sf = st.GetFrame (2);
MethodBase mb = sf.GetMethod ();
return mb.DeclaringType.ToString () + "." + mb.Name;
}
}
public int StaticValue
{
get { return staticData[InstanceId]; }
set { staticData[InstanceId] = value; }
}
public MyStaticInt (int initializationValue)
{
if (!staticData.ContainsKey (InstanceId))
staticData.Add (InstanceId, initializationValue);
}
}
Can be used this way...
class Program
{
static void Main (string[] args)
{
// Only one static variable is possible per Namespace.Class.Method scope
MyStaticInt localStaticInt = new MyStaticInt (0);
// Working with it
localStaticInt.StaticValue = 5;
int test = localStaticInt.StaticValue;
}
}
It's not a perfect solution, but an interesting toy.
You can only have one static variable of this type per Namespace.Class.Method scope. Won't work in property methods - they all resolve to the same name - get_InstanceId.
I developed a static class that deals with this problem in a fairly simple manner:
using System.Collections.Generic;
using System.Runtime.CompilerServices;
public static class StaticLocal<T>
{
static StaticLocal()
{
dictionary = new Dictionary<int, Dictionary<string, Access>>();
}
public class Access
{
public T Value { get; set; }
public Access(T value)
{
Value = value;
}
}
public static Access Init(T value, [CallerFilePath]string callingFile = "",
[CallerMemberName]string callingMethod = "",
[CallerLineNumber]int lineNumber = -1)
{
var secondKey = callingFile + '.' + callingMethod;
if (!dictionary.ContainsKey(lineNumber))
dictionary.Add(lineNumber, new Dictionary<string, Access>());
if (!dictionary[lineNumber].ContainsKey(secondKey))
dictionary[lineNumber].Add(secondKey, new Access(value));
return dictionary[lineNumber][secondKey];
}
private static Dictionary<int, Dictionary<string, Access>> dictionary;
}
It can be implemented within a method like this:
var myVar = StaticLocal<int>.Init(1);
Console.Writeline(++myVar.Value);
On each subsequent call to the method, the value contained in myVar.Value will be the last one it was set to so repeated calls will cause it to output a sequence of natural numbers. The Init() function only sets the value if it has not been previously initialized. Otherwise it just returns a reference to an object containing the value.
It makes use of the [CallerFilePath], [CallerMemberName] and [CallerLineNumber] attributes to track which item in the dictionary is being referred to. This eliminates the possibility of collisions between methods with the same names or calls from the same line numbers.
A few caveats about its usage:
This approach is probably overkill for most instances where you would want to use static local variables. Its use of indirection to deal with separate files, methods and lines might be unnecessary for your project, in which case you can simplify it to meet your needs.
I haven't seen a good generic solution to this yet so I thought I'd come up with my own. I should note however that for the most part(not always) needing static local variables is probably a sign that you should refactor your code for the reasons that have been stated by many people; state is something for the object not a method. I do however like the idea of limiting the scope of variables.
Without further ado:
public class StaticLocalVariable<T>
{
private static Dictionary<int, T> s_GlobalStates = new Dictionary<int, T>();
private int m_StateKey;
public StaticLocalVariable()
{
Initialize(default(T));
}
public StaticLocalVariable( T value )
{
Initialize(value);
}
private void Initialize( T value )
{
m_StateKey = new StackTrace(false).GetFrame(2).GetNativeOffset();
if (!s_GlobalStates.ContainsKey(m_StateKey))
{
s_GlobalStates.Add(m_StateKey, value);
}
}
public T Value
{
set { s_GlobalStates[m_StateKey] = value; }
get { return s_GlobalStates[m_StateKey]; }
}
}
This isn't thread safe of course but it wouldn't take too much work to make it so. It can be used like so:
static void Main( string[] args )
{
Console.WriteLine("First Call:");
Test();
Console.WriteLine("");
Console.WriteLine("Second Call:");
Test();
Console.ReadLine();
}
public static void Test()
{
StaticLocalVariable<int> intTest1 = new StaticLocalVariable<int>(0);
StaticLocalVariable<int> intTest2 = new StaticLocalVariable<int>(1);
StaticLocalVariable<double> doubleTest1 = new StaticLocalVariable<double>(2.1);
StaticLocalVariable<double> doubleTest2 = new StaticLocalVariable<double>();
Console.WriteLine("Values upon entering Method: ");
Console.WriteLine(" intTest1 Value: " + intTest1.Value);
Console.WriteLine(" intTest2 Value: " + intTest2.Value);
Console.WriteLine(" doubleTest1 Value: " + doubleTest1.Value);
Console.WriteLine(" doubleTest2 Value: " + doubleTest2.Value);
++intTest1.Value;
intTest2.Value *= 3;
doubleTest1.Value += 3.14;
doubleTest2.Value += 4.5;
Console.WriteLine("After messing with values: ");
Console.WriteLine(" intTest1 Value: " + intTest1.Value);
Console.WriteLine(" intTest1 Value: " + intTest2.Value);
Console.WriteLine(" doubleTest1 Value: " + doubleTest1.Value);
Console.WriteLine(" doubleTest2 Value: " + doubleTest2.Value);
}
// Output:
// First Call:
// Values upon entering Method:
// intTest1 Value: 0
// intTest2 Value: 1
// doubleTest1 Value: 2.1
// doubleTest2 Value: 0
// After messing with values:
// intTest1 Value: 1
// intTest1 Value: 3
// doubleTest1 Value: 5.24
// doubleTest2 Value: 4.5
// Second Call:
// Values upon entering Method:
// intTest1 Value: 1
// intTest2 Value: 3
// doubleTest1 Value: 5.24
// doubleTest2 Value: 4.5
// After messing with values:
// intTest1 Value: 2
// intTest1 Value: 9
// doubleTest1 Value: 8.38
// doubleTest2 Value: 9
Along the lines of Henk's and BarretJ's answer, I think you can avoid the initialization cost and come even closer by using a property,
private Regex myReg = null;
private Regex MyReg
{
get {
if (myReg == null)
myReg = new Regex("\\(copy (\\d+)\\)$");
return myReg;
}
}
Then just use MyReg (note the uppercase 'M' in MyReg) everywhere in your code. The nice thing about this solution is that (although the getter is a function call under the hood) the semantics of properties means that you get to write code as if MyReg was a variable.
The above is how I setup "runtime constants" that require a one-time initialization at runtime.
I do the same thing using nullable types, too. For example,
private bool? _BoolVar = null;
private bool BoolVar
{
get {
if (_BoolVar.HasValue)
return (bool)_BoolVar;
_BoolVar = /* your initialization code goes here */;
return (bool)_BoolVar;
}
}
Then just use BoolVar like a regular normal bool in your code. I don't use internal _BoolVar (the backing store for the BoolVar property) because I just don't need to, remember this is like a runtime constant, so there is no setter. However, if I needed to change the value of the runtime constant for some reason, I'd do that directly on the nullable variable _BoolVar.
The initialization could be pretty involved. But it's only executed one time and only on the first access of the property. And you have the choice of forcing the re-initialization of the runtime constant value by setting _BoolVar back to null.