Does C# support the use of static local variables?

2019-01-22 19:04发布

问题:

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 asked previously, and I looked but didn't find a dupe.

Is it possible for me to create a static local variable in C#? If so, how?

I have a static private method that is used rarely. the static method uses a Regular Expression, which I would like to initialize once, and only when necessary.

In C, I could do this with a local static variable. Can I do this in C#?

When I try to compile this code:

    private static string AppendCopyToFileName(string f)
    {
        static System.Text.RegularExpressions.Regex re =
            new System.Text.RegularExpressions.Regex("\\(copy (\\d+)\\)$");
    }

...it gives me an error:

error CS0106: The modifier 'static' is not valid for this item


If there's no local static variable, I suppose I could approximate what I want by creating a tiny new private static class, and inserting both the method and the variable (field) into the class. Like this:

public class MyClass 
{
    ...
    private static class Helper
    {
        private static readonly System.Text.RegularExpressions.Regex re =
            new System.Text.RegularExpressions.Regex("\\(copy (\\d+)\\)$");

        internal static string AppendCopyToFileName(string f)
        {
            // use re here...
        }
    }

    // example of using the helper
    private static void Foo() 
    {
       if (File.Exists(name)) 
       {
           // helper gets JIT'd first time through this code
           string newName = Helper.AppendCopyToFileName(name);
       }
    }
    ...
}

Thinking about this more, using a helper class like this there would yield a bigger net savings in efficiency, because the Helper class would not be JIT'd or loaded unless necessary. Right?

回答1:

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.



回答2:

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.



回答3:

Why not create a static readonly member on your class and initialize it in a static constructor maybe?

This will give you the same performance benefit - it will only get initialised once.



回答4:

Not in C#, only in Visual Basic .NET:

Sub DoSomething()
  Static obj As Object
  If obj Is Nothing Then obj = New Object
  Console.WriteLine(obj.ToString())
End Sub  

VB.NET have lot of nice things that C# does not have, thats why I choose VB.NET.



回答5:

What about this, since you only want it to be initialized if it's used:

private static System.Text.RegularExpressions.Regex myReg = null;
public static void myMethod()
{
    if (myReg == null)
        myReg = new Regex("\\(copy (\\d+)\\)$");
}


回答6:

C# doesn't support static local variables. In addition to what has been posted above, here's a link to an MSDN blog entry on the subject:
http://blogs.msdn.com/b/csharpfaq/archive/2004/05/11/why-doesn-t-c-support-static-method-variables.aspx



回答7:

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.



回答8:

Sure. You just have to declare the private static variable outside of the method.

    private static readonly System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex( "\\(copy (\\d+)\\)$" );
    private static string AppendCopyToFileName( string f )
    {
        //do stuff.
    }

This is effectively what you are doing with the only difference being that "re" has visibility to the entire class as opposed to just the method.



回答9:

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


回答10:

Nesting the related members in an inner class as you have shown in question is the cleanest most probably. You need not push your parent method into inner class if the static variable can somehow get the caller info.

public class MyClass 
{
    ...
    class Helper
    {
        static Regex re = new Regex("\\(copy (\\d+)\\)$");
        string caller;

        internal Helper([CallerMemberName] string caller = null)
        {
            this.caller = caller;
        }

        internal Regex Re
        {
            //can avoid hard coding
            get
            {
                return caller == "AppendCopyToFileName" ? re : null;
            }
            set
            {
                if (caller == "AppendCopyToFileName")
                    re = value;
            }
        }
    }


    private static string AppendCopyToFileName(string f)
    {
        var re = new Helper().Re; //get
        new Helper().Re = ...; //set
    }


    private static void Foo() 
    {
        var re = new Helper().Re; //gets null
        new Helper().Re = ...; //set makes no difference
    }
}
  1. You can avoid hard coding of method names in the property using some expression tree tricks.

  2. You can avoid the helper constructor and make the property static, but you need to get the caller info inside the property, via using StackTrace.

Lastly, there is always const possible inside a method, but then one, it's not variable, two, only compile time constants are allowed. Just stating.



回答11:

Three years later...

You can approximate it with a captured local variable.

 class MyNose
    {
        private static void Main()
        {
            var myNose= new MyNose();
            var nosePicker = myNose.CreatePicker();

            var x = nosePicker();
            var y = nosePicker();
            var z = nosePicker();
        }

        public Func<int> CreatePicker()
        {
            int boog = 0;

            return () => boog++;
        }
    }


回答12:

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:

  • As others have stated, it's worthwhile to consider whether what you're doing really requires the use of static local variables. Their use can sometimes be a sign that your design is flawed and could use some refactoring.
  • This method of dealing with the problem involves a couple layers of indirection, slowing down the execution of your program. It should only be used if it justifies that cost.
  • Static local variables can help you to deal with having too many members declared in your class, thus compartmentalizing them where they're used. This should be weighed against the execution time cost but can sometimes be worth it. On the other hand, having so many members being declared within a class may be an indication of design problems worth considering.
  • Because these values continue to remain in memory after their methods complete execution you must be mindful that using them to store large chunks of memory will prevent garbage-collection until the program completes, thus diminishing your available resources.

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.



回答13:

It is duplicate from Why doesn't C# support local static variables like C does?

But still, I think you will find my answer useful.



标签: c# static