-
Notifications
You must be signed in to change notification settings - Fork 1k
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: Allow multiple top-level statements per assembly #4163
Comments
The C# compiler isn't aware of the project file. It's up to whatever build environment (like MSBuild) to interpret the project file and to pass the appropriate options to the compiler. If I understand what you're asking for correctly what you want is to be able to have multiple files with top level statements but you'd only ever compile one of them at a time. That feels like something that should be handled by the build tool by only including the specific source file that contains the top level statements that you want to have compiled. |
I understand that. I envision that the project property would map to the corresponding compiler option, similar to how
Not exactly. I want all of them compiled, but only one of them (or some other file) to be considered as entry point. This is analogous to the |
One thought to potentially help with the generation of unique names while not breaking existing dependencies on the |
Allow multiple top-level statements per assembly
Summary
Currently, only one file in an assembly (or project) can have top-level statements, as they were introduced in C# 9 (#2765). This makes sense from a logical perspective, because having multiple entry points would raise ambiguity as to which one should be executed. However, there are certain use cases where this ambiguity can easily be resolved and having multiple top-level programs could be highly beneficial (explained in the next section).
Motivation
Allowing multiple top-level programs could help in some scenarios.
For example, project SmallSharp provides a way to have multiple top-level programs in a project, offering a convenient experience for writing scripts while still benefitting from MSBuild infrastructure and IDEs. It does this by excluding all root
*.cs
files from compilation (to prevent the compilation error), except the file matching the current launch profile ($(ActiveDebugProfile)
). The launch profiles are generated automatically via a source generator, one for each program file in the root directory.This allows the user to select which program to run in the IDE (by selecting one in the dropdown) or via CLI (via
--launch-profile
option). While functional, it has a few downsides:Another use case is my own experimental project Hallstatt. The idea behind it is to offer a way for developers to write tests using top-level statements, avoiding a ton of boilerplate and inherent limitations of methods/attributes. Of course, because currently only one file is allowed to have top-level statements, it severely limits the usability of the library. Additionally, the approach used by SmallSharp is not applicable here, because all of the files containing top-level statements need to be compiled, not just one.
Essentially, in both of these use cases there is no ambiguity problem, because:
However, because of the compilation limitation that requires that only one file may contain top-level statements, they both have to rely on ugly workarounds.
Detailed design
The suggestion is to keep the existing behavior which limits top-level statements to one file, but add a compiler option and/or project property to lift it:
(property names are up for consideration)
Effectively, this should allow all files that contain top-level statements to compile, each generating a dynamic
<$Program>
class with aMain
method. Another property (StartupFile
) will then have to be used to instruct which file to run. IfStartupFile
is not specified, butAllowMultipleTopLevelStatements
istrue
, a compilation error will need to be raised.Running
dotnet run
on this project will run theMain
method generated by the top level statements insideFileThatContainsTheTopLevelStatementsThatNeedToRun.cs
.This is quite similar to an already existing
-main
compiler option (and<StartupObject>
property), except that it should take a file path (compilation unit) instead of a class name, because the user doesn't know what is the name of the compiler-generated class.This would allow SmallSharp to change this property dynamically without having to exclude files from compilation, avoiding the associated drawbacks.
In case with Hallstatt, it would allow the library to use
StartupFile
to specify its own entry point, while at the same time using reflection to run user-defined top-level statements by searching forMain
methods inside compiler-generated<$Program>
classes.Drawbacks
Currently, the compiler-generated class for top-level statements seems to always have the same name. If we introduce the option to allow multiple to be defined, we would have to make sure that the generated names are unique. However, some developers may already have taken dependency on the existing class name through reflection, which may break their code.
Alternatives
Other designs have not been considered.
The impact of not doing this: developers will need to find ways to work around this limitation by hacking with MSBuild targets, ultimately achieving suboptimal and potentially error-prone results.
Unresolved questions
Given that there may be multiple top-level programs, what naming schema should the auto-generated class be using?
Design meetings
The text was updated successfully, but these errors were encountered: