Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Please allow to define static local variables in functions, like C/C++ #10552

Closed
ygc369 opened this issue Apr 14, 2016 · 24 comments
Closed

Please allow to define static local variables in functions, like C/C++ #10552

ygc369 opened this issue Apr 14, 2016 · 24 comments

Comments

@ygc369
Copy link

ygc369 commented Apr 14, 2016

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);
}
@DavidArno
Copy link

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.

@ygc369
Copy link
Author

ygc369 commented Apr 14, 2016

@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.

@DavidArno
Copy link

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

@axel-habermaier
Copy link
Contributor

@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).

@DavidArno
Copy link

@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.

@HaloFour
Copy link

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.

@FrankHB
Copy link

FrankHB commented Apr 15, 2016

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.

@sunnycase
Copy link

@FrankHB +1

@MgSam
Copy link

MgSam commented Apr 15, 2016

Duplicate of #49

@DerpMcDerp
Copy link

Perl's state keyword is superior to C++'s static keyword:

public static class Blah {
    private static void Foo() {
        System.Func<int> fn = () => {
            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.

@HaloFour
Copy link

@DerpMcDerp But also unnecessary given that you can just define i in the parent method and you automatically gain that functionality.

@DerpMcDerp
Copy link

DerpMcDerp commented Apr 29, 2016

@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.

@HaloFour
Copy link

@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.

@FrankHB
Copy link

FrankHB commented May 4, 2016

@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.

@jrmoreno1
Copy link
Contributor

@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

@NetMage
Copy link

NetMage commented Feb 23, 2017

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.

@Ultrahead
Copy link

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).

@ygc369 ygc369 closed this as completed Jun 30, 2017
@NetMage
Copy link

NetMage commented Jun 30, 2017

Why is it still allowed to close issues without comment?

@sharwell
Copy link
Member

sharwell commented Jun 30, 2017

@NetMage GitHub allows users to close their own issues. We can only set policy regarding the action of a subset of those users. 😄

@NetMage
Copy link

NetMage commented Jun 30, 2017

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.

@sharwell
Copy link
Member

@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).

@miyu
Copy link

miyu commented Jul 7, 2018

Follow-up discussion: dotnet/csharplang#832

@syndrome
Copy link

syndrome commented Jul 1, 2024

I don't quite understand how the original discussion evolved. Having local static variables is merely a syntactic sugar.
For example
public class MyClass {

void MyFunc() {
static readonly MyType _cached;
if(_cached is null) _cached = compute(...);
// use _cached
}

}

can be easily processed to an already supported pattern
public class MyClass {
static readonly MyType _cached_MyFunc23873; // generated "unique identifier"

void MyFunc() {
if(_cached_MyFunc23873 is null) _cached_MyFunc23873 = compute(...);
// use _cached_MyFunc23873
}
}

I don't understand what's with the anti-pattern concerns mentioned above. The feature would offer a cognitive and organizational benefits by making sure a specific class field is accessible only to the function that declared it. Initialization and readonly modifier are not a concern, because this is only a syntactic sugar that guarantees a variable won't be used elsewhere (the field is strongly coupled with a function). The point of guaranteeing uniqueness is to prevent clashes when two functions locally declare static variables which bear the same name.

I need to reiterate once more how strongly this affects the readability of a code that is vastly more complex than this example.

@CyrusNajmabadi
Copy link
Member

@syndrome this issue was closed 7 years ago. Please use the existing discussions on dotnet/csharplang for your thoughts. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests