Create, edit and run multiple C# top-level programs in the same project, respecting per-file #:package
references and #:property
project values 😍
To ensure the long-term sustainability of this project, use of SmallSharp requires an Open Source Maintenance Fee. While the source code is freely available under the terms of the MIT License, all other aspects of the project --including opening or commenting on issues, participating in discussions and downloading releases-- require adherence to the Maintenance Fee.
In short, if you use this project to generate revenue, the Maintenance Fee is required.
To pay the Maintenance Fee, become a Sponsor.
C# top-level programs allow a very intuitive, simple and streamlined experience for quickly spiking or learning C#. The addition of dotnet run app.cs in .NET 10 takes this further by allowing package references and even MSBuild properties to be specified per file:
#:package Humanizer@2.14.1
using Humanizer;
var dotNet9Released = DateTimeOffset.Parse("2024-12-03");
var since = DateTimeOffset.Now - dotNet9Released;
Console.WriteLine($"It has been {since.Humanize()} since .NET 9 was released.");
Editing these standalone files in VSCode, however, is suboptimal compared with the full C#
experience in Visual Studio. In Visual Studio, though, you can only have one top-level program
in a project, and as of now, you cannot leverage the #:package
and #:property
directives
at all.
SmallSharp allows dynamically selecting the file to run, right from the Start button/dropdown
(for compilation and launch/debug). It also automatically restores the #:package
references so
the project system can resolve them, and even emits the #:property
directives if present to customize
the build as needed.
This list is automatically kept in sync as you add more .cs
files to the project. When you select
one target C# file, that becomes the only top-level program to be compiled, so you don't have to
modify any of the others since they automatically become None items.
Tip
An initial build after selection change migh be needed to restore the packages and compile the selected file
All compile files directly under the project directory root are considered top-level programs for selection and compilation purposes. If you need to share code among them, you can place additional files in subdirectories and those will behave like normal compile items.
SmallSharp works by just installing the SmallSharp nuget package in a C# console project.
Recommended installation as an SDK:
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="SmallSharp" Version="2.0.0" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
</Project>
Or as a regular package reference:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SmallSharp" Version="*" />
</ItemGroup>
</Project>
Keep adding as many top-level programs as you need, and switch between them easily by simply selecting the desired file from the Start button dropdown.
This nuget package leverages in concert the following standalone and otherwise unrelated features of the compiler, nuget and MSBuild:
- The C# compiler only allows one top-level program per compilation.
- Launch profiles (the entries in the Run dropdown) are populated from the Properties\launchSettings.json file
- Whenever changed, the dropdown selection is persisted as the
$(ActiveDebugProfile)
MSBuild property in a file named after the project with the.user
extension - This file is imported before NuGet-provided MSBuild targets
Using the above features in concert, SmallSharp essentially does the following:
-
Emit top-level files as a
launchSettings.json
profile and set the$(ActiveDebugProfile)
. -
Exclude
.cs
files at the project level from being included as<Compile>
by the default SDK includes and include them explicitly as<None>
instead so they show up in the solution explorer. This prevents the compiler from causing an error for multiple top-level programs. -
Explicitly include as
<Compile>
only the$(ActiveDebugProfile)
property value. -
Emit
#:package
and#:property
directive to an automatically importedobj\SmallSharp.targets
file -
SmallSharp MSBuild SDK automatically imports the
SmallSharp.targets
file, which causes a new restore to automatically happen in Visual Studio, bringing all required dependencies automatically.
This basically mean that this it will also work consistently if you use dotnet run
from the command-line,
since the "Main" file selection is performed exclusively via MSBuild item manipulation.
Tip
It is recommended to keep the project file to its bare minimum, usually having just the SmallSharp
SDK reference, and do all project/package references in the top-level files using the #:package
and
#:property
directives for improved isolation between the top-level programs.
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="SmallSharp" Version="2.0.0" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
</Project>