Allow local variables (within method blocks) to be marked as static
, readonly
, or static readonly
.
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;
...
}
}
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):
Love this idea :+1:
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
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.)
:bulb: If you are going to allow static
(without the readonly
modifier), you should probably update this to allow static volatile
as well.
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.
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?
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.
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? :stuck_out_tongue:
@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
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.
@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
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.
Should this work with properties? If so, then what rules make sense? Would the following work?
c#
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
This is a proposal for two distinct features, which are expressed individually in #115 and #10552.
Most helpful comment
:bulb: If you are going to allow
static
(without thereadonly
modifier), you should probably update this to allowstatic volatile
as well.