Finally with .NET 10, we can stop using Powershell for single-file applications, and start using the one language to rule them all - C#! It works cross-platform and yes, you can provide compiler directives
#!/usr/bin/env dotnet run
#:property EnableDefaultEmbeddedResourceItems=false
#:property Nullable=enable
#:package Humanizer@*
using Humanizer;
using System;
int number = 42;
Console.WriteLine($"The number {number} in words is: {number.ToWords()}");
To invoke:
dotnet demo.cs
Better still, register .cs files in the registry, to permit double-click to run. Save the following as register-cs.cs and invoke with:
dotnet register-cs.cs
#!/usr/bin/env dotnet run
#:property EnableDefaultEmbeddedResourceItems=false
#:property NoWarn=CA1416
using System;
using System.IO;
using Microsoft.Win32;
// --- Configuration ---
const string CommandName = "CSharpScript.Run";
const string Extension = ".cs";
// --- Registry Setup ---
Console.WriteLine("--- Registering .cs Extension with dotnet.exe ---");
try
{
// 1. Determine the path to dotnet.exe
string? dotnetPath = Environment.ExpandEnvironmentVariables(@"%ProgramFiles%\dotnet\dotnet.exe");
if (!File.Exists(dotnetPath))
{
// Fallback check for x86 systems
dotnetPath = Environment.ExpandEnvironmentVariables(@"%ProgramFiles(x86)%\dotnet\dotnet.exe");
if (!File.Exists(dotnetPath))
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"Error: Cannot find dotnet.exe at expected paths ({Environment.ExpandEnvironmentVariables(@"%ProgramFiles%\dotnet\dotnet.exe")}).");
Console.WriteLine("Please ensure .NET is installed.");
Console.ResetColor();
return;
}
}
// The command format: "C:\Path\To\dotnet.exe" "%1"
string commandValue = $"\"{dotnetPath}\" \"%1\"";
Console.WriteLine($"Found dotnet.exe: {dotnetPath}");
Console.WriteLine($"Command: {commandValue}");
// The root key for user-specific file associations
RegistryKey? classesRoot = Registry.CurrentUser.OpenSubKey("Software\\Classes", true);
if (classesRoot == null)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Error: Could not open HKEY_CURRENT_USER\\Software\\Classes. Check permissions.");
Console.ResetColor();
return;
}
// --- Step 2: Define the Command (CSharpScript.Run) ---
// Create/open CSharpScript.Run key
using (RegistryKey? commandKey = classesRoot.CreateSubKey(CommandName))
{
if (commandKey == null) throw new Exception($"Failed to create key: {CommandName}");
commandKey.SetValue(null, "C# Script Runner (dotnet)");
// Set the icon
using (RegistryKey? openKey = commandKey.CreateSubKey("shell\\open"))
{
openKey?.SetValue("Icon", dotnetPath);
}
// Set the actual command executed when opening the file
using (RegistryKey? commandPath = commandKey.CreateSubKey("shell\\open\\command"))
{
if (commandPath == null) throw new Exception("Failed to create command key path.");
commandPath.SetValue(null, commandValue);
}
}
Console.WriteLine($"Successfully registered the execution command '{CommandName}'.");
// --- Step 3: Associate the .cs extension ---
// Create/open the .cs extension key
using (RegistryKey? extensionKey = classesRoot.CreateSubKey(Extension))
{
if (extensionKey == null) throw new Exception($"Failed to create key: {Extension}");
// Set the default value of the .cs key to the name of our command
extensionKey.SetValue(null, CommandName);
}
Console.WriteLine($"Successfully associated extension '{Extension}' with '{CommandName}'.");
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("\nREGISTRATION COMPLETE.");
Console.WriteLine("-----------------------------------------------------------------");
Console.WriteLine($"You can now run C# scripts by typing: .\\yourfile{Extension}");
Console.WriteLine("Note: Running this way will open a separate console window.");
Console.WriteLine("You may need to restart your terminal or log off/on for changes to fully take effect.");
Console.ResetColor();
}
catch (System.Security.SecurityException)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("\nSECURITY ERROR: You might need to run this command with administrative privileges.");
Console.WriteLine("Try running your terminal as Administrator and executing the script again.");
Console.ResetColor();
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"An unexpected error occurred: {ex.Message}");
Console.ResetColor();
}