-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Proposal: Nested local functions and type declarations #259
Comments
There have certainly been occasions in the past where I've wished that C# supported nested methods which had automatic access to the variables declared in the outer scope without the need to pass them as parameters. Even in old BASIC programming one could do something similar with the crude but effective GOSUB command. As we already have anonymous methods and lambda expressions, it doesn't seem much of a conceptual jump to support 'named' nested methods as well so I'd certainly be in favor of that part of the proposal However, I don't remember ever wishing that I could declare whole types within a method which, unless they were restricted in some way, would add significant extra complexity to the language. Are there any particular use cases you think such a feature would address? Incidentally, I know that Java supports something called 'local classes' but, on the rare occasions I've needed to program in that language, I've never found a use for them. |
I think the local function problem is pretty well solved by C#'s closures
The local class problem isn't one i encounter often, but sometimes i need a narrowly scoped class to fulfill an interface contract. Aside from interface shims i can't think of any good use cases for local classes that are not solved by C#'s anonymous classes already. |
Named nested methods would have one advantage over anonymous methods or lambda expressions in that you wouldn't need to assign them to a compatible delegate before you could invoke them. As you know, if there's no suitable predefined delegate type in the .NET framework, you have to come up with your own. On the other hand, you'd need to specify all parameter types because there would be no way that the compiler could infer them. Anonymous methods also have some restrictions - you can't use |
I don't see a benefit here that justifies the complexity. I do see somewhat of the point of having the local class (and more importantly struct) simply because anonymous classes have readonly fields, but I don't think there are very many algorithms that actually need them. Most of those algorithms would probably be better off with LINQ and immutable types. |
@mirhagk: No, unfortunately they don't. Consider ref, out, pointer, or params parameters. Those are not valid generic arguments and therefore Action and Func can't be used. |
Although using System;
delegate void MyDelegate(ref int a);
class Program
{
static int a = 2;
static void Main()
{
MyMethod();
}
static void MyMethod()
{
MyDelegate d = (ref int b) => ++b;
d(ref a);
Console.WriteLine(a); // 3
}
} If you tried to replace If nested methods were introduced, I imagine that the above program would look like this if one uses C# 6.0's 'expression bodied' function feature: using System;
class Program
{
static int a = 2;
static void Main()
{
MyMethod();
}
static void MyMethod()
{
void Nested(ref int b) => ++b;
Nested(ref a);
Console.WriteLine(a); // 3
}
} A bit cleaner, perhaps :) |
Okay that makes sense. If this is to happen, I'd like to see it in tandem with allowing statements at the top level, ie that class/method definitions are basically treated on the same level as statements. That would allow you to define functions outside of a class, allow for this, and allow statements at the top level (which makes it possible to treat C# as a scripting language, mentioned in #98). |
@mirhagk I am very wary of allowing top level statements and classless functions in C#. I can see endless abuse in codebases with little benefit to normal programs. I love C# but i don't think scriptifying it makes sense at all, we have many scripting languages that are better suited to the task. |
@AlgorithmsAreCool The thing is C# is already used as a scripting language. Scriptcs and LinqPad both offer scripting capabilities, but with some hacks to allow top level statements (linqpad makes you choose expression vs statements vs program and wraps them as appropriate, scriptcs basically allows bare statements). Classless functions already essentially exist with static classes. The difference between a static class and a namespace with top level functions is basically nothing. EDIT: Perhaps the C# grammar would support them, but it would be disallowed for most project types. You could only allow it in a "scripting" context. |
On my side I would like to add that the local functions are extremely useful in functional programming as recursive functions. We use this approach very often. Actually the underlying construct is more like: Func<int, int> Fib = null; To allow recursive calls. int Fib(int n) => n > 2 ? Fib(n-1) + Fib(n-2) : 1; +1 for this feature - worth implementing even as a simple syntactic sugar. |
The one big advantage that local functions would have over delegates is that delegate invocation is much more expensive than a direct method call. Aside the closure capabilities there really isn't a great deal of benefit of that over a regular private static method. In the case of closures would a local function also have the notion of a capture list, like with #117 ? |
@gafter I don't disagree. Given that local functions will be much more limited in how they can be used as opposed to delegates it's probably not necessary, either. Instead of promoting the variables to fields in a closure state machine they could silently be public void Foo() {
int x = 10;
int IncrementBy(int value) {
x += value;
return x;
}
int result = IncrementBy(1);
Debug.Assert(result == x);
} could be translated into: private static void Foo_IncrementBy(int value, ref int x) {
x += value;
return x;
}
public void Foo() {
int x = 10;
int result = Foo_IncrementBy(1, ref x);
Debug.Assert(result == x);
} If the local function would reference members of the containing type then Additionally, for perf purposes, I'd like to add that invoking the local function always be done with |
Capture lists do not sound like a good idea for recursive local functions unless tail calls are aplicable, IMO non-tail-recursion should disable capture lists (if implemented) to avoid wasting stack memory. |
I think this would be a very useful feature. This kind of coding can improve organization and reduce namespace pollution. Additionally, If these local functions were permitted to be generic, it would open up some nice scenarios that are not covered by the |
This change addresses #259: below issues related to diagnostics generated for analyzer exceptions from third party analyzers. 1.Suppression of duplicate exception diagnostics: Current mechanism did the suppression in SuppressMessageState based on unique reported messages. This is obviously incorrect as an exception diagnostic will be reported non-suppressed and suppressed on subsequent queries to SuppressMessageState.IsDiagnosticSuppressed. 2.The IDE diagnostic service has multiple layers where document/project diagnostics are filtered and these analyzer exception diagnostics were getting dropped at various places. So this change moves the exception diagnostics generation + reporting out of the regular analyzer diagnostic pipeline and in line with analyzer load failure diagnostics reporting in VS: 1.Add an event handler to AnalyzerDriverHelper to report analyzer exception diagnostics to interested clients. 2.Listen to these diagnostic events in IDE diagnostic service and wrap them with relevant workspace/project argument and generate updated events. 3.Add an AbstractHostDiagnosticUpdateSource in Features layer to listen and report analyzer exception diagnostic events from diagnostic service. Additionally, removal of an analyzer reference in workspace will clean up the diagnostics for the analyzers belonging to that analyzer reference. 4.Listen to exception diagnostic events in command line compiler and report as regular diagnostics. Added typw AbstractHostDiagnosticUpdateSource can be extended in future to report other kind of host diagnostics which are not related to a project/document/analyzer.
I think it would be nice if that code warned twice: "Unreachable local method declared." for both It should also complain about As to knowing whether the defined method is a local function or member method, yes that is admittedly a potential source of confusion for users. It is even worse if local functions are nested inside each other ( |
Can we also have Local expression bodied Properties like-
|
You could use an anonymous delegate or lambda for that purpose:
That would create a closure over I think the real question here is: do you have a compelling use case for local properties (regardless of how they are expressed)? What could you do with them that you couldn't do with local functions? |
We have local functions lined up for C# 7 already done in the future branch, so I'm going to close this as done. If someone wants local type declarations, please open a separate issue for that. |
Good one... |
This should be tagged with |
This issue is so weird because there's no actual proposal here, just a "hey, let's do local functions!" - What's the impetus for the proposal? What would the semantics be? What would the advantage(s) be over the alternatives, such as simply inferring that It's good to notify the community you're going to do a feature, but this isn't enough information for an intelligent discussion. |
Note: This was opened a year ago. And closed a month ago. So it's probably too late for any further discussions. In fact, you're just saying "why didn't anybody tell me?" |
In case it will help anyone, I found slightly more information in "local-functions.md". See also #2930 Edit: Aha! found the real proposal at #3911. |
I suggest another syntax to local functions to be unnested dotnet/csharplang#1329 |
Extend the languages to support the declaration of functions and types in block scope. Local functions would be capable of using captured variables from the enclosing scope.
The text was updated successfully, but these errors were encountered: