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

Allow local variables to be static and/or readonly (deeper scoping) #49

Closed
myermian opened this issue Jan 21, 2015 · 11 comments
Closed

Comments

@myermian
Copy link

Proposal

Allow local variables (within method blocks) to be marked as static, readonly, or static readonly.

Purpose

This would allow a developer to have the advantage of static and readonly fields, but limit their usage to a fine-grained scope.

Example:

class Foo
{
    public void Method()
    {
        static readonly var codes = new[] { 1, 7, 10 };
    }
}

This would help alleviate situations such as reallocating a new array (and assigning the value to fibonaccis) upon each execution of Method():

class Foo
{
    public void Method()
    {
        // This should be static readonly, but only accessible in this method.
        var fibonaccis = new int[] { 0, 1, 2, 5, 8, 13, ...,  701408733 };

        ...
    }
}

Or, it could help avoid potential issues with static field being accessible outside of its intended scope, purposefully or accidentally:

class Foo
{
    // This variable should only be used in Method().
    // Please do not change its value on me somewhere else!
    private static int _methodCount = 0;

    public void Method()
    {
        var count = ++_methodCount;

        ...
    }
}

Implementation

The Roslyn compiler could recognize the syntax and generate the necessary IL code, just as it does for automatic properties, async/await, foreach, etc.

Originally, the discussion from codeplex talked about the potential issues and confusion, and the final thought I had was to lift the local variable as a Lazy<T> and for this feature to be purely syntactic sugar. After researching how VB.NET implements local static variables, the compiler does a bit more work to generate the correct IL code. See this article: http://weblogs.asp.net/psteele/7717. I think the same thought should be put into C#.

While the article only talks about static local variables, I think that there would be real value to also allow the readonly keyword to be used as well. The complexity would grow, of course, because we have four possible situations (instead of just two):

  • static without initialization (already supported by VB.NET).
  • static with initialization (already supported by VB.NET).
  • readonly with initialization (not supported by any language).
  • static readonly with initialization (not supported by any language).
@matwilko
Copy link

Love this idea 👍

My ten cents on how to somewhat simplify the idea so that the problems of eager/lazy initialization hopefully go away is to just restrict the semantics of the declaration to mean that it is a private instance/static field on the class, but lexically scoped only to the method.

This makes the hoist completely trivial - just give it an "unpronounceable" name - and the compiler rewrites references to it with the new name in the method.

It also solves the initialization problem as the semantics are the same with any other instance/static field. If there is an initializer, the restrictions on it are the same as for regular initializers, and it is run at the same time as other instance/static initializers.

If there isn't an initializer for non-readonly fields, it's up to the method to do any necessary initialization and locking etc. if thread safety is required. Alternatively, constructors could be allowed to set the field via MethodName.fieldname = blah; , much like they can for get-only auto-props now, although this falls apart when more than one overload uses an identically named method-local field :/

If the developer wants lazy semantics for the field - it's just the same as with a "normal" field, declare it as type Lazy, and refer to field.Value in the method.

Multi-threaded scenarios are also explicitly not handled, as is the case with normal fields.

I think all this fulfils the principle of least astonishment better as the syntactic sugar is incredibly thin, and it means that we get better scoping without having to worry about some complicated codegen going on underneath (which could change assumptions about things like memory use/locality etc.)

@sharwell
Copy link
Member

💡 If you are going to allow static (without the readonly modifier), you should probably update this to allow static volatile as well.

@sharwell
Copy link
Member

For static local variables with an initializer, it seems like you could hoist the local to be a static member of a compiler-generated private nested class. This way you can still use a static constructor (of the nested class) to initialize the value, but in many cases avoid initializing the value until the first time the method is called.

@matwilko
Copy link

Don't we then reintroduce the problem of making it harder for the developer to know easily at a glance when the field is going to be initialized? And what if I explicitly don't want the lazy semantics?

@paulomorgado
Copy link

Other than constants that are literally replaced at compile time (as all constants are, but these wouldn't have a field), if static scoped fields are to added, why not instance fields?

@matwilko, I agree with @sharwell. It makes a lot more sense to expect the field to be initialized on the first execution of the method. After all, it's where it's declared on the source. If you want/need it to be initialized elsewhere, than it's not logically scoped to the method.

@s-aida
Copy link

s-aida commented Jan 23, 2015

On a silly note, if this feature is introduced as static variables in a local scope , then for VB would it be a shared variable that is actually NOT shared by anything else? 😛

@HaloFour
Copy link

@shunsukeaida VB.NET already has Static variables which behave as described by this feature request. The keyword was inherited from pre-.NET versions of VB.

Implementing ReadOnly variables would be a new feature for VB.NET, though.

For readonly variables I'm personally a fan of the Apple Swift syntax which uses let as an alternative to var, e.g.:

var x = 1;
let y = 2;
x = 3; // legal
y = 4; // compiler error, y is readonly

@s-aida
Copy link

s-aida commented Jan 23, 2015

I know of VB's static but oh I didn't think it was equivalent to this feature which asked for non-reallocable local variable, and VB's static is something you'd use in a pretty procedural programming.

But yeah, the third example is what VB's static is for.

@matwilko
Copy link

@paulomorgado Fair enough :) Was just trying to simplify it down enough to avoid needing the debate on how and when to initialize the field when it's an instance field (the static case was always going to be pretty simple)

The use of a Lazy to get the lazy-initialization for instance fields just feels a bit heavy to me, it adds a layer of indirection, (potentially) reduces locality, and requires a delegate to be instantiated, which all seems a bit much for what should be a lightning fast and memory-efficient field access.

We could add manual locking with only an extra bool and object (to act as initialization check and locking object)? This keeps the field local to the object in memory, requires minimal extra memory to be used, and only results in one extra allocation.

@bondsbw
Copy link

bondsbw commented Jul 12, 2016

Should this work with properties? If so, then what rules make sense? Would the following work?

public int Foo
{
    get { return foo; }
    set
    {
        static int foo = 0;
        foo = value;
    }
}

Same question vice-versa, and also with event add/remove accessors.

Related: #850, #12361

@MadsTorgersen
Copy link
Contributor

This is a proposal for two distinct features, which are expressed individually in #115 and #10552.

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