File-Based Apps
https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/
https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/tutorials/file-based-programs
It only took 10 years but Microsoft did what I had always wanted them to do and come up with a way to make single file C# "scripts" a reality without relying on a third-party system like csharpscript. I would have loved this at my first job where we just needed a lot of strongly-typed scripts that were first class citizens for COM Interop, but we got by with Python.
I spun together a small idea with Copilot to read prompt input and spit out the token count of that input:
#!/usr/bin/dotnet run
// Token Counter - counts whitespace-separated tokens in input text.
//
// Usage:
// dotnet run TokenCounter.cs -- "some text to count" ← CLI argument
// dotnet run TokenCounter.cs -- --input path.txt ← read from file
// echo "some text" | dotnet run TokenCounter.cs ← piped / scripted input
// cat file.txt | dotnet run TokenCounter.cs
#:package Microsoft.ML.Tokenizers@2.0.0
#:package Microsoft.ML.Tokenizers.Data.O200kBase@2.0.0
using Microsoft.ML.Tokenizers;
string? text = null;
if (args.Length > 0)
{
if (args[0] == "--input" || args[0] == "-i")
{
if (args.Length < 2)
{
Console.Error.WriteLine("Error: --input requires a path argument.");
return 1;
}
string path = args[1];
if (!File.Exists(path))
{
Console.Error.WriteLine($"Error: file not found: {path}");
return 1;
}
text = await File.ReadAllTextAsync(path);
}
else if (args[0] == "--help" || args[0] == "-h")
{
Console.WriteLine("""
Token Counter — counts whitespace-separated tokens in input text.
Usage:
dotnet run TokenCounter.cs -- <text>
dotnet run TokenCounter.cs -- --input <path>
<command> | dotnet run TokenCounter.cs
Options:
-i, --input <path> Read input from a file
-h, --help Show this help message
""");
return 0;
}
else
{
// Treat all args as the text to count (join with spaces)
text = string.Join(' ', args);
}
}
else if (!Console.IsInputRedirected)
{
Console.Error.WriteLine("Error: no input provided. Pass text as an argument, use --file, or pipe input.");
Console.Error.WriteLine("Run with --help for usage.");
return 1;
}
else
{
// Read from stdin (piped / scripted)
text = await Console.In.ReadToEndAsync();
}
var tokenizer = TiktokenTokenizer.CreateForEncoding("o200k_base");
int count = tokenizer.CountTokens(text);
Console.WriteLine($"Tokens: {count}");
return 0;
Minus the comment-block. documentation, that's 60-ish lines of code. As with all agentically-developed things, I don't think this is a reasonably "complete" script; I'd want to do more with the prompt, leverage System.CommandLine or CommandLineParser, etc., but this is a pretty good example of what you can do with one file.
This fits in really well with a .NET shop's agentic flow where you can have a hook that does token analysis without having to learn Python.