Proposal: Persisted Method-Scoped Locals #8047
Replies: 3 comments 8 replies
-
I know it's something of a cliche at this point, but you could do this today with a Roslyn analyzer. Ultimately, what's the difference between a "persisted method local" and a class instance field? The only difference is that the latter has limited scope, and any time you want a change to the language that is nothing more than a restriction on code that is currently legal, an analyzer is the way to do it. With such an analyzer you could write the following: public class MyClass
{
[ScopedTo(nameof(MyMethod))]
int methodLocal = 0;
public void MyMethod()
{
methodLocal++;
Console.WriteLine(methodLocal);
}
public void MyMethod2()
{
methodLocal++; // Compiler error: `methodLocal` is scoped to `MyMethod` only
Console.WriteLine(methodLocal);
}
} |
Beta Was this translation helpful? Give feedback.
-
See: #832 |
Beta Was this translation helpful? Give feedback.
-
I'll throw in that I like the feature in principle. I think that it marries with the idea of local functions which are scoped to the method also. I encounter this pattern usually in two situations
That being said, my usual workaround is placing the dangerous field in its own class with safe accessors. My workaround:
Because of that I am not sure this feature will improve the situation enough to justify it. But i said the same thing about Local Functions also so 🤷♀️ |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Summary:
This proposal introduces a new language feature in C# that allows developers to declare locals within a method that retain their values across method invocations. The scope of these locals can be finely controlled, enabling access from method overloads and overrides based on the developer's requirements. The feature is designed to enhance method encapsulation, facilitating more stateful method implementations while maintaining clear and concise method signatures.
Motivation:
In certain scenarios, developers might require the ability to retain state within a method across invocations without exposing this state at the class level or resorting to static fields, which are shared across all instances. Such capability would be particularly useful in recursive methods, stateful iterators, or methods with a significant computational state that should persist between calls. Current workarounds involve using class-level fields or static variables, which can lead to less intuitive code and potential state management issues, especially in multi-threaded contexts.
Syntactic Examples:
Design Specification:
Syntax: Introduce a new keyword
persist
to declare a method-scoped persisted local. This local is only visible within the method and/or its specified overloads/overrides. We also allowstatic persist
for static persistance.Visibility Control: The visibility of persisted locals can be precisely managed to define their accessibility scope. For instance, declaring
persist internal int x
enables the variablex
to be accessed within overloads or overrides contained within the same assembly. Usingpersist private int x
restricts the visibility to the declaring method itself, excluding any of its overloads or overrides. Conversely,persist protected int x
allows the variable to be accessible in overriding methods, fostering inheritance compatibility. By default, overloads can access persisted locals unless theprivate
modifier is explicitly applied, ensuring a flexible yet secure approach to method-specific state management.Implementation Mechanics and Initialization: The C# compiler will internally generate a private nested class for each method requiring persisted locals, encapsulating the persisted state within fields.
persist private
is used, the initial value is hardcoded into the field initializer of the nested class, ensuring that the value is tightly bound to the method.internal
,protected
, etc.), a parameterized constructor in the nested class is utilized. The first method invocation dictates the initial value, which is then passed to the nested class constructor.Code Example Demonstrating Initialization Mechanisms:
In this example:
MyMethod
uses apersist private
local, initializing it directly within the nested class's field declaration (methodLocal = 10;
).MyOtherMethod
demonstrates a non-private persistent local. The initial value is not hardcoded but passed to the nested class's constructor, allowing dynamic initialization (new MyOtherMethodState(20);
).Beta Was this translation helpful? Give feedback.
All reactions