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…