Does C# support the use of static local variables?

后端 未结 12 898
眼角桃花
眼角桃花 2020-12-14 17:28

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

相关标签:
12条回答
  • 2020-12-14 17:53

    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.

    0 讨论(0)
  • 2020-12-14 17:53

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

    0 讨论(0)
  • 2020-12-14 17:58

    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.

    0 讨论(0)
  • 2020-12-14 17:58

    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.

    0 讨论(0)
  • 2020-12-14 17:59

    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
    
    0 讨论(0)
  • 2020-12-14 18:01

    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.

    0 讨论(0)
提交回复
热议问题