Static locals are cool

Or so says a sticky note that I stuck on my monitor on Friday to remind me to blog about them. Static locals are local variables whose values are preserved across calls to a method. For example, the following method will print the sequence 0, 1, 2, 3, 4, 5, 6, etc. on successive calls:

Sub PrintNextNumber()
    Static x As Integer = 0

    Console.WriteLine(x)
    x += 1
End Sub

What basically happens with static locals is that we create a hidden field in the containing class to store the value across calls to the method. Thus, static locals are really most useful when you have a type-level value that you need to store that you want only scoped to a particular method. I thought to blog about this because I was working on finalizing my managed VB parser sample and I needed to have a lookup table for type characters when scanning type characters. I could have written it as:

Class Scanner
    Private _TypeCharacterTable As Hashtable
    Private ReadOnly Property TypeCharacterTable As HashTable
        Get
            If _TypeCharacterTable Is Nothing Then
                … initialize table …
            End If

            Return _TypeCharacterTable
        End Get
    End Property

    Private Function IsTypeCharacter(ByVal c As Char) As TypeCharacter
        Return _TypeCharacterTable(c)
    End Function
End Class

But with static locals, you can be more compact:

Class Scanner
    Private Function IsTypeCharacter(ByVal c As Char) As TypeCharacter
        Static TypeCharacterTable As Hashtable

        If _TypeCharacterTable Is Nothing Then
            … initialize table …
        End If

        Return TypeCharacterTable(c)
    End Function
End Class

One thing my code above doesn’t do is consider multi-threaded situations. We’ll automatically generate thread-safe code for static local initializers, but since I have to do extra initialization above, I should really synchronize the initialization. One other thing to keep in mind is that static locals are only preserved per-instance in instance methods. Calling PrintNextNumber on two different instances of the containing type will not give sequential numbers. If the containing method is shared, however, the calling instance doesn’t matter.

Random factoid #1: Static locals are a major reason why we used the term “Shared” for shared members instead of “Static.” We also felt that “Shared” is a lot more descriptive than “Static,” and I wonder whether C# would have chosen that term if they hadn’t been stuck with the legacy of C, but… This is probably the most painful keyword divergence between the two languages in terms of documentation.

Random factoid #2: Implementing static locals in the compiler was a gigantic pain in the ass, and we had a lot of arguments about whether they were worth the effort. There are times that I haven’t been entirely convinced about it, but I think my thoughts on this are changing…

18 thoughts on “Static locals are cool

  1. Corrado Cavalli

    Can’t remember last time i’ve used ‘static’ on my code, in a OOP world i always use my own static variable.

    What I always read instead is: "Now add a static variable (shared in VB)…" this happens also on VB docs, so my point of view is that you could have saved a lot of effort using static as C# equivalent.

    Reply
  2. Robert Jacobson

    I don’t understand the point you’re trying to make with the code examples. You could rewrite the first example much more compactly:

    Class Scanner

    Private _TypeCharacterTable As Hashtable

    Private Function IsTypeCharacter(ByVal c As Char) As TypeCharacter

    If _TypeCharacterTable Is Nothing Then

    … initialize table …

    End If

    Return _TypeCharacterTable(c)

    End Function

    End Class

    Any VB static variable can easily be rewritten as a private class-level variable. (If I understand correctly, that’s what the VB compiler does anyway behind the scenes.) For the benefit of clarity and avoiding confusion with the C# "static" keyword, I’d rather just use a private variable instead.

    Reply
  3. NInputer

    I wander why there are two different keywords for static members. Static and Shared. Although they are not the same meaning, but at least they have the same name.

    The other question is when I use static variables in a multithread procedure, I found the behavior of a static variable is just like a field of the class.

    Reply
  4. paulvick

    Robert: It’s really a question of encapsulation. It seems bad to me to have a private field in a class whose initialization state is never concretely known except to a single function. Thus, it’s possible that other code in the class might start accidentally using the array and, depending on method call order, might work for some time before crashing. The nice thing about static locals is that they are encapsulated nicely.

    Reply
  5. Urs Eichmann

    Did you ever try to XML-serialize a class with a static variable in it? It won’t work… at least not in VB.net 2003. I hope this will be improved in Whidbey.

    Reply
  6. Eric Mutta

    Paul:>Random factoid #2: Implementing static locals in the compiler was a gigantic pain in the ass…

    Hmmm, I’m curious. What issues did you face? Isn’t it just a case of treating it like a declarations level variable but with scope extending to one procedure instead of all procedures? Some naming scheme would be needed for disambiguation of the name, if the variable was shadowing another one before it was "moved" to the declarations level.

    Another idea that just popped into my head is to simplify things by treating it like a normal local variable, but you adjust the memory/register allocator to handle static locals slightly differently.

    In any case I’m sure you probably already considered those options and the reasons for the difficulty are more esoteric.

    Paul:> …I was working on finalizing my managed VB parser sample…

    Interesting. By "sample" do you mean you’ll share it at some point? I’ve been looking for uses of VB in compiler implementation and if you are doing such a thing, it would be great to hear about the experience.

    Reply
  7. paulvick

    Urs: Good point about the serialization. It’s something we’re talking about, we’ll have to get back to you on that!

    Eric: It was a pain in the ass because normally there is a very nice separation between declaration-level stuff and method-level stuff. When we’re compiling, we can generally ignore everything inside of methods while we’re doing declaration-level stuff like building and binding symbols. Once all the declaration-level stuff is done, then we start looking inside methods. At which point we discover that we’ve got a static local, which requires us to add something to the containing type which, in theory, has already been finished. So we end up having to do some things out of order to make it work.

    As for the sample, yes, my goal is to post it to be freely available so people can use it to write tools that manipulate VB code. Soon, I hope.

    Reply
  8. Anon

    Robert,

    <quote>

    Any VB static variable can easily be rewritten as a private class-level variable.

    </quote>

    Sure, but use of Static allows you to limit the scope of the variable.

    Seeya

    Reply
  9. Branco Medeiros

    Mike Schinkel wrote:

    > I had static locals in CA-Clipper in 1990 and

    > loved them. Glad to finally get them in VB,

    > 15 years later… :)

    Mike, VB has static locals at least since VB 3.0 (not sure about older versions of the language).

    What VB.Net ditched away though was Static *methods* — methods were *all* variables were static.

    Hmmm, coming to think of the difficulties of copilation cited by Paul Vick, I guess static methods would be easier to compile than normal methods with static locals…

    Regards,

    Branco.

    Reply
  10. Stephen Price

    What about Static local variables inside of a shared method? Is that a good thing to do? It works but i’ve been told it smells of bad coding practice.

    Its also something C# won’t let you do… Which seems funny to me. Worth using or avoiding?

    Reply
  11. paulvick

    I don’t see any reason to avoid it for either instance or shared methods… You can do the same with a field (which is what happens under the covers), so what’s the problem?

    Reply
  12. Pingback: Dan McKinley

  13. MarkT

    Paul:>Random factoid #2: Implementing static locals in the compiler was a gigantic pain in the ass…

    Interesting perspective… I had never considered language design to be the result of what the compiler writer could "easily" do. That explains a lot of weird decisions in a lot of languages.

    In a perfect (computing) world, the compiler writer would go through whatever pain was necessary so that the resulting language would be clear, safe, and easy to use. Of course, given perfection to begin with, that pain would be small because HIS tools would work just the way he wanted.

    At any rate, my vote is a strong positive for local static variables. It makes code UGLY to define a variable with scope much larger than needed. Also, it makes it less maintainable and more prone to misuse.

    Reply
  14. Pingback: Code Heaven

  15. Pingback: Anonymous

  16. Joe Cloonan

    Microsoft VB.net help topic about Static Variables states that you should avoid using Static variables in Shared methods. It appears that it doesn’t extend the life of the variable because the life of a local variable in a Shared method is already extended through the end of the life of the running application. What about caching and threading? I’m having a hard time finding clear information about the consequences of using Static variables in Shared methods in VB.net. Is it harmful? Can you offer any insight?

    Reply

Leave a Reply