tags:

views:

203

answers:

6

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?

+4  A: 

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.

Developer Art
+5  A: 

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 compiled once.

Bobby
+1 for mentioning to make it readonly.
Thomas
yep, I know that static readonly members get initialized once. But the goal is to not initialize it *at all*, unless necessary. In C, I could do that with a local static.
Cheeso
@Cheeso: Make the static member a `Lazy<T>`. For pre-4.0, you can make an `internal` class in your assembly with the same members as `Lazy<T>` (the basic implementation is pretty simple): http://msdn.microsoft.com/en-us/library/dd642331(VS.100).aspx
280Z28
Oh! Lazy<T> is new for .NET 4.0. Ah...thanks.
Cheeso
+3  A: 

No, C# does not support this. You can come close with:

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'. I don't think C or Java will behave any different with regards to when this is initialized.

Henk Holterman
I was hoping to save the initialization cost. But thinking it through, it would be a larger net savings to embed that static method into its own class, which is then used only "sometimes". The savings there is even larger than the savings related to not initializing the Regex unless necessary. And the thread-safety issue is solved, as well.
Cheeso
A: 

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.

Thomas
A: 

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+)\\)$");
}
BarrettJ
yes, that would do it. In a multi-thread scenario, though, I think there might be a race condition there.
Cheeso
What race condition might that be? You're initializing it to the same value...
micahtan
+1  A: 

VB.NET supports static local variables. Code like this:

Class Test
  Public Sub Method()
    Static stat As Integer
    stat += 1
  End Sub
End Class

gets rewritten by the compiler to this:

Class Test
  Private $STATIC$Method$2001$stat As Integer
  Public Sub Method()
    $STATIC$Method$2001$stat += 1
  End Sub
End Class

You can do this yourself in C#, you however cannot reproduce the scope limitation. Note that the hoisted field is not static in VB.NET, every class object gets its own static locals. This is not terribly intuitive, consider what is the right choice for you. And ponder thread safety for a bit, VB.NET static locals are not thread-safe.

Hans Passant