The static local variables can only be used in the functions which define them. C/C++ have this feature.
For example:
void foo()
{
static int call_times=0;
call_times++;
Console.Writeline("Function foo has been called {0} times", call_times);
}
Static local variables are a way of maintaining state between calls to the same function. They are needed in C. They aren't needed in either C++ (though they exist there because of the origins of that language) or C#. You can achieve the same maintenance of state either through fields in object instances, or with static fields in a class.
@DavidArno
I know what you said. But I think static local variable can make code more readable, it means this variable can only used in the specified function.
public void Foo() => PrivateFoo.Foo();
private static class PrivateFoo
{
private static int CallTimes;
public static void Foo() =>
Console.WriteLine($"Function Foo has been called {++CallTimes} times");
}
Of course, just because the language already supports such a construct, doesn't mean one should ever write code like this...
@DavidArno: How is that more readable than
public void Foo()
{
static int callTimes = 0;
Console.WriteLine($"Function Foo has been called {++callTimes} times");
}
The more interesting question is whether there would be _one_ callTimes
instance for all invocations of Foo
, or one per object (since Foo
is not static
).
@axel-habermaier,
I'm not saying it's more readable. I'm saying that this functionality can already be achieved with the language, as is.
Since it wraps global static state within a single, very hard to mock, function, it's a pretty nasty implementation of the singleton anti-pattern. Changes to the language should enhance it. Simplifying the syntax around an already-implementable anti-pattern isn't my idea of enhancing a language.
Note that VB.NET has always had this feature, it was inherited from VB5/6:
Public Sub Bar()
Static CallTimes As Integer
CallTimes += 1
Console.WriteLine($"This method was called {CallTimes} times.")
End Sub
Just pointing that out. I'm not endorsing the feature. I'm personally not a fan of hiding shared state in the middle of a function like that. I considered it bad practice back when I was writing VB5/6.
It should be noted that there are not too many differences between C and C++ in this area. In C++, static data members can have same storage duration category (static or thread-local) with namespace scope or block scope variables, the only differences are scoping (which has an effect on locality of names, i.e. information hiding), and (vs. block scope ones) the time of initialization. I'm not a fan of this feature but it is strange to allow static fields while disallowing static local variables, since the latter actually always does better than the former in several cases due to the differences above.
Note the style to return a reference to a local static variable (delayed initialization, only when needed) is a variant of so-called monostate pattern, which is not quite the same to singleton pattern, but a better replacement in many cases, esp. since C++11, the initialization can be thread-safe easily, while the traditional singleton is still having a lot of problems (and very hard to do right) in C++, Java, C#, so on.
@FrankHB +1
Duplicate of https://github.com/dotnet/roslyn/issues/49
Perl's state
keyword is superior to C++'s static
keyword:
``` C#
public static class Blah {
private static void Foo() {
System.Func
static int a = 0;
return a++;
};
System.Console.Write("{0} {1} {2}", fn(), fn(), fn());
}
public static void Bar() {
Foo();
Foo();
}
}
```
Blah.Bar()
should print " 0 1 2 0 1 2" (i.e. behaves like Perl's state
) instead of " 0 1 2 3 4 5" (i.e. behaves like C++'s static
). In other words, if a static appears in a lambda, it should get initialized per instance of lambda rather than per group of lambas.
@DerpMcDerp But also unnecessary given that you can just define i
in the parent method and you automatically gain that functionality.
@HaloFour Except that defining i
in the parent method initializes it when the parent method is called rather than when the lambda is called so it's not necessarily the same thing.
@DerpMcDerp Considering how frequently such a problem is encountered and necessarily solved (read: never) I'd say that the few extra lines of code necessary to manage lazy initialization is a very small price to pay. The net effect is identical anyway.
@DerpMcDerp You are effectively overloading the static
keyword with meaning that quite different with prior art, which may easily lead to more confusion. If you need that feature, taking another keyword will be better.
@DavidArno:
Your example isn't quite the same thing as VB's static...Not to mention creating a static class to hold a single value seems like too much work, most people are simply going to make it a regular instance field and leave a comment saying it should only be used in X and hope that is well enough. Particularly since the value that you are creating is truly static (life time of the app) while VB's is scoped to a particular INSTANCE of a class and it's lifetime.
public class NotTheSame
{
public void Foo() => PrivateFoo.Foo();
private static class PrivateFoo
{
static PrivateFoo() {
CallTimes = 600;
}
private static int CallTimes;
public static void Foo() =>
Console.WriteLine($"Function Foo has been called {++CallTimes} times");
}
var x1 = new NotTheSame();
var x2 = new NotTheSame();
x1.Foo(); // 601
x2.Foo(); // 602
In the past it seems like the main arguments against adding this could all be applied to local functions as well, so now that they are included I would vote yes (obviously in C# 8 at this point) if there were formal voting.
I'd use static locals with recursion, but now local functions are included I can handle such use cases with them within the scope of each instance. But there could be cases where shared access among instances is needed, so it's a +1 for me (specially given that it's already present in VB).
Why is it still allowed to close issues without comment?
@NetMage GitHub allows users to close their own issues. We can only set policy regarding the action of a subset of those users. :smile:
That would seem to be a problem - I open an issue, it is closed as Duplicate of someone else's issue. They close that issue and my interest in the issue is effectively lost.
@NetMage If this issue is of interest to you, I would recommend filing on dotnet/csharplang, since that's where proposals for changes to the language are getting filed now. I searched but didn't see a duplicate (doesn't mean one doesn't exist).
Follow-up discussion: https://github.com/dotnet/csharplang/issues/832
Most helpful comment
It should be noted that there are not too many differences between C and C++ in this area. In C++, static data members can have same storage duration category (static or thread-local) with namespace scope or block scope variables, the only differences are scoping (which has an effect on locality of names, i.e. information hiding), and (vs. block scope ones) the time of initialization. I'm not a fan of this feature but it is strange to allow static fields while disallowing static local variables, since the latter actually always does better than the former in several cases due to the differences above.
Note the style to return a reference to a local static variable (delayed initialization, only when needed) is a variant of so-called monostate pattern, which is not quite the same to singleton pattern, but a better replacement in many cases, esp. since C++11, the initialization can be thread-safe easily, while the traditional singleton is still having a lot of problems (and very hard to do right) in C++, Java, C#, so on.