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…
Yeah, I wondered about those too last year and dug into the IL to see what the compiler was doing behind the scenes:
http://weblogs.asp.net/psteele/articles/7717.aspx
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.
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.
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.
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.
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.
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.
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.
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
I had static locals in CA-Clipper in 1990 and loved them. Glad to finally get them in VB, 15 years later… 🙂
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.
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?
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?
Pingback: Dan McKinley
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.
Pingback: Code Heaven
Pingback: Anonymous
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?