{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/csharplang/issues/1440","repository_url":"https://api.github.com/repos/dotnet/csharplang","labels_url":"https://api.github.com/repos/dotnet/csharplang/issues/1440/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/csharplang/issues/1440/comments","events_url":"https://api.github.com/repos/dotnet/csharplang/issues/1440/events","html_url":"https://github.com/dotnet/csharplang/issues/1440","id":311819788,"number":1440,"title":"Proposal: Non-static entry point","user":{"login":"sylveon","id":6440374,"avatar_url":"https://avatars3.githubusercontent.com/u/6440374?v=4","gravatar_id":"","url":"https://api.github.com/users/sylveon","html_url":"https://github.com/sylveon","followers_url":"https://api.github.com/users/sylveon/followers","following_url":"https://api.github.com/users/sylveon/following{/other_user}","gists_url":"https://api.github.com/users/sylveon/gists{/gist_id}","starred_url":"https://api.github.com/users/sylveon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sylveon/subscriptions","organizations_url":"https://api.github.com/users/sylveon/orgs","repos_url":"https://api.github.com/users/sylveon/repos","events_url":"https://api.github.com/users/sylveon/events{/privacy}","received_events_url":"https://api.github.com/users/sylveon/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":2,"created_at":"2018-04-06T01:15:52Z","updated_at":"2018-04-07T08:42:05Z","closed_at":null,"author_association":"NONE","body":"## Summary\r\n[summary]: #summary\r\n\r\nThis suggestion aims to permit the use of a Main method without the `static` attribute, if the class in which the entry point resides has a parameter-less constructor.\r\n\r\n## Motivation\r\n[motivation]: #motivation\r\n\r\nCurrently, accessing non-static members of the class in which Main resides requires manually creating an instance of it. It's an error that many beginners I saw on a community-ran C# Discord make, and that I sometimes accidentally make myself. Adding this would prevent those kinds of mistakes, as well as remove the manual instance creation required to access these if for any reason they cannot be marked static.\r\n\r\nOne of the case I can think off my head is if the class inherits an abstract class from an external library or the program itself which has virtual methods (why would you do this to a entry point class is out of the scope of this proposal). Instead of having to do `new Program().VirtualMethod();`, `this.VirtualMethod();` would suffice.\r\n\r\n## Detailed design\r\n[design]: #detailed-design\r\n\r\nKnowing the limitation of the CLR to have a static entry point, the solution would lean towards something like the recently introduced async main, which is just syntactic sugar for something similar to this:\r\n\r\n```cs\r\npublic static int Main(string[] args) => MainAsync(args).GetAwaiter().GetResult();\r\n\r\npublic static async Task<int> MainAsync(string[] args)\r\n{\r\n    // Method body\r\n}\r\n```\r\n\r\nWhich means, if this proposal is implemented, this (currently invalid) code\r\n```cs\r\nclass Program\r\n{\r\n    private readonly _rng = new Random();\r\n\r\n    public int Main(string[] args)\r\n    {\r\n        return _rng.Next();\r\n    }\r\n}\r\n```\r\n\r\nwould, when compiled, end up more akin to\r\n```cs\r\nclass Program\r\n{\r\n    private readonly _rng = new Random();\r\n\r\n    public int Main(string[] args)\r\n    {\r\n        return _rng.Next();\r\n    }\r\n\r\n    public static int Main(string[] args) => new Program().Main(args);\r\n}\r\n```\r\n\r\nEvidently, we would be able to use the other variants of Main too without the static modifier (async, void return, parameter-less)\r\n\r\nIf the class has a custom-specified parameter-less constructor, it will be ran (maybe it would be good to show a warning here, specifying that the constructor will run before the non-static Main).\r\n\r\nIf the class does not have a parameter-less constructor (for example, it is marked abstract, or it has custom constructors), the compiler would error if a non-static Main is trying to be used.\r\n\r\nIf both a non-static Main and static Main exists, a CS0017 error will be emitted, and the user would need to remove one of them to continue, or use the `/main` compiler switch to select another class as entry point. (this was just quickly though of, as I am not aware of what happens currently if both a async and sync entry point are declared. Maybe it would be  better to use the same behavior here.)\r\n\r\n---\r\n\r\nI am open to suggestions and criticism."},"comment":{"url":"https://api.github.com/repos/dotnet/csharplang/issues/comments/379451223","html_url":"https://github.com/dotnet/csharplang/issues/1440#issuecomment-379451223","issue_url":"https://api.github.com/repos/dotnet/csharplang/issues/1440","id":379451223,"user":{"login":"theunrepentantgeek","id":1272094,"avatar_url":"https://avatars2.githubusercontent.com/u/1272094?v=4","gravatar_id":"","url":"https://api.github.com/users/theunrepentantgeek","html_url":"https://github.com/theunrepentantgeek","followers_url":"https://api.github.com/users/theunrepentantgeek/followers","following_url":"https://api.github.com/users/theunrepentantgeek/following{/other_user}","gists_url":"https://api.github.com/users/theunrepentantgeek/gists{/gist_id}","starred_url":"https://api.github.com/users/theunrepentantgeek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/theunrepentantgeek/subscriptions","organizations_url":"https://api.github.com/users/theunrepentantgeek/orgs","repos_url":"https://api.github.com/users/theunrepentantgeek/repos","events_url":"https://api.github.com/users/theunrepentantgeek/events{/privacy}","received_events_url":"https://api.github.com/users/theunrepentantgeek/received_events","type":"User","site_admin":false},"created_at":"2018-04-07T08:42:05Z","updated_at":"2018-04-07T08:42:05Z","author_association":"NONE","body":"This would break existing code - the `static Main()` method doesn't have to be on any particular class (i.e. it doesn't have to be a member of `static class Program`). The only restriction is that there can be only one (unless you forgo compiling in Visual Studio and use `/main` from the command-line).\r\n\r\nWhile I don't know if it is a common pattern, I've seen a bunch of console apps where each of the *modes* of the application are implemented by a different class, each with an entry point called `Main()`.\r\n\r\nTo illustrate, here's how a program like **git** would be built if it were rewritten in C# following this approach:\r\n\r\n``` csharp\r\npublic static class Program\r\n{\r\n    public static int Main(string[] args) { ... }\r\n}\r\n\r\npublic class PullMode\r\n{\r\n    public int Main(string [] args) { ... }\r\n}\r\n\r\npublic class FetchMode\r\n{\r\n    public int Main(string [] args) { ... }\r\n}\r\n\r\npublic class CommitMode\r\n{\r\n    public int Main(string [] args) { ... }\r\n}\r\n```\r\nand so on.\r\n\r\nIf non-static `Main()` methods were considered valid entry points, code like this would fail to compile on the new version of the compiler.\r\n\r\nSince the team (rightly!) holds backward compatibility in very high regard, I suspect this suggestion won't progress as the benefit (if any) is minor."}}
{"actor":{"display_login":"theunrepentantgeek"}}