C# Console and Environment

📖 5 min read

Console Operations

Basic Input/Output

// Output
Console.WriteLine("Hello, World!");     // With newline
Console.Write("No newline");            // Without newline
Console.WriteLine($"Value: {value}");   // String interpolation

// Input
string? input = Console.ReadLine();     // Read line (nullable)
ConsoleKeyInfo key = Console.ReadKey(); // Read single key
Console.ReadKey(true);                  // Suppress key echo

// Error output (separate stream)
Console.Error.WriteLine("Error message");

// Standard streams
TextWriter stdout = Console.Out;
TextWriter stderr = Console.Error;
TextReader stdin = Console.In;

Formatting Output

// Numeric format strings
Console.WriteLine("Currency: {0:C}", 1234.56);      // $1,234.56
Console.WriteLine("Decimal: {0:D8}", 42);           // 00000042
Console.WriteLine("Scientific: {0:E2}", 12345.67); // 1.23E+004
Console.WriteLine("Fixed: {0:F2}", 3.14159);        // 3.14
Console.WriteLine("Percent: {0:P1}", 0.1234);       // 12.3%
Console.WriteLine("Hex: {0:X}", 255);               // FF
Console.WriteLine("Number: {0:N0}", 1234567);       // 1,234,567

// Alignment
Console.WriteLine("{0,-10} {1,10}", "Left", "Right");
Console.WriteLine("{0,10:C}", 42.5);  // Right-aligned currency

// Date/time formatting
Console.WriteLine("{0:D} at {1:HH:mm}", DateTime.Now, DateTime.Now);
Console.WriteLine($"ISO: {DateTime.Now:yyyy-MM-ddTHH:mm:ss}");

// String interpolation with format
Console.WriteLine($"Price: {price:C2}");
Console.WriteLine($"Date: {date:yyyy-MM-dd}");
Console.WriteLine($"Aligned: {name,-20} {value,10:N2}");

Console Colors

// Set colors
Console.ForegroundColor = ConsoleColor.Green;
Console.BackgroundColor = ConsoleColor.Black;
Console.WriteLine("Colored text");
Console.ResetColor();

// Available colors
// Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta,
// DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta,
// Yellow, White

// Pattern: save and restore
var originalFg = Console.ForegroundColor;
var originalBg = Console.BackgroundColor;
try
{
    Console.ForegroundColor = ConsoleColor.Red;
    Console.WriteLine("Error!");
}
finally
{
    Console.ForegroundColor = originalFg;
    Console.BackgroundColor = originalBg;
}

Cursor and Window

// Cursor positioning
Console.SetCursorPosition(10, 5);  // Column, Row
(int left, int top) = Console.GetCursorPosition();

// Cursor visibility
Console.CursorVisible = false;  // Hide cursor

// Clear operations
Console.Clear();  // Clear entire screen

// Window size (platform-dependent)
try
{
    Console.WindowWidth = 120;
    Console.WindowHeight = 30;
    Console.BufferWidth = 120;
    Console.BufferHeight = 300;
}
catch (PlatformNotSupportedException)
{
    // Not supported on all platforms
}

// Beep (Windows)
Console.Beep();
Console.Beep(frequency: 800, duration: 200);

Interactive Console Patterns

// Simple menu
Console.WriteLine("1. Option A");
Console.WriteLine("2. Option B");
Console.WriteLine("3. Exit");
Console.Write("Choice: ");

string? choice = Console.ReadLine();
switch (choice)
{
    case "1": HandleOptionA(); break;
    case "2": HandleOptionB(); break;
    case "3": return;
    default: Console.WriteLine("Invalid choice"); break;
}

// Password input (no echo)
Console.Write("Password: ");
var password = new StringBuilder();
ConsoleKeyInfo key;
while ((key = Console.ReadKey(true)).Key != ConsoleKey.Enter)
{
    if (key.Key == ConsoleKey.Backspace && password.Length > 0)
    {
        password.Length--;
        Console.Write("\b \b");
    }
    else if (!char.IsControl(key.KeyChar))
    {
        password.Append(key.KeyChar);
        Console.Write("*");
    }
}
Console.WriteLine();
string pwd = password.ToString();

// Progress indicator
for (int i = 0; i <= 100; i++)
{
    Console.Write($"\rProgress: {i}%");
    Thread.Sleep(50);
}
Console.WriteLine();

Environment

Environment Variables

// Read
string? value = Environment.GetEnvironmentVariable("MY_VAR");
string? pathValue = Environment.GetEnvironmentVariable("PATH");

// Read with target (Windows)
string? userVar = Environment.GetEnvironmentVariable(
    "MY_VAR", EnvironmentVariableTarget.User);
string? machineVar = Environment.GetEnvironmentVariable(
    "MY_VAR", EnvironmentVariableTarget.Machine);

// Set (process scope by default)
Environment.SetEnvironmentVariable("MY_VAR", "value");

// Set with target (Windows, requires elevation for Machine)
Environment.SetEnvironmentVariable(
    "MY_VAR", "value", EnvironmentVariableTarget.User);

// Delete (set to null)
Environment.SetEnvironmentVariable("MY_VAR", null);

// Get all environment variables
foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables())
{
    Console.WriteLine($"{entry.Key}={entry.Value}");
}

Command Line Arguments

// In Main method
static void Main(string[] args)
{
    foreach (string arg in args)
    {
        Console.WriteLine(arg);
    }
}

// Anywhere in the application
string[] allArgs = Environment.GetCommandLineArgs();
// Note: allArgs[0] is the executable path

// Simple argument parsing
var arguments = new Dictionary<string, string?>();
for (int i = 0; i < args.Length; i++)
{
    if (args[i].StartsWith("--"))
    {
        string key = args[i][2..];
        string? value = i + 1 < args.Length && !args[i + 1].StartsWith("--")
            ? args[++i]
            : null;
        arguments[key] = value;
    }
}

Special Folders

// User folders
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string downloads = Path.Combine(home, "Downloads");  // No built-in constant

// Application data
string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string commonAppData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);

// System folders
string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
string system = Environment.GetFolderPath(Environment.SpecialFolder.System);
string windows = Environment.GetFolderPath(Environment.SpecialFolder.Windows);

// Temp folder
string temp = Path.GetTempPath();

// Current directory
string current = Environment.CurrentDirectory;
// or: Directory.GetCurrentDirectory()

System Information

// Machine info
string machineName = Environment.MachineName;
string userName = Environment.UserName;
string userDomain = Environment.UserDomainName;

// OS info
OperatingSystem os = Environment.OSVersion;
Console.WriteLine($"Platform: {os.Platform}");
Console.WriteLine($"Version: {os.Version}");

// Runtime info
Console.WriteLine($".NET Version: {Environment.Version}");
Console.WriteLine($"64-bit OS: {Environment.Is64BitOperatingSystem}");
Console.WriteLine($"64-bit Process: {Environment.Is64BitProcess}");
Console.WriteLine($"Processor Count: {Environment.ProcessorCount}");

// Memory
long workingSet = Environment.WorkingSet;  // Bytes

// Uptime
TimeSpan uptime = TimeSpan.FromMilliseconds(Environment.TickCount64);

// Exit codes
Environment.ExitCode = 0;  // Success
Environment.Exit(1);       // Exit immediately with code

Process Information

using System.Diagnostics;

// Current process
Process current = Process.GetCurrentProcess();
Console.WriteLine($"PID: {current.Id}");
Console.WriteLine($"Name: {current.ProcessName}");
Console.WriteLine($"Memory: {current.WorkingSet64 / 1024 / 1024} MB");
Console.WriteLine($"Threads: {current.Threads.Count}");
Console.WriteLine($"Start Time: {current.StartTime}");

Binary Serialization

BinaryWriter and BinaryReader

For custom binary formats and protocol implementations.

// Write binary data
using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms);

writer.Write(42);              // Int32 (4 bytes)
writer.Write("hello");         // Length-prefixed string
writer.Write(3.14159);         // Double (8 bytes)
writer.Write(true);            // Boolean (1 byte)
writer.Write((byte)255);       // Byte
writer.Write(new byte[] { 1, 2, 3 });  // Raw bytes

// Read binary data
ms.Position = 0;
using var reader = new BinaryReader(ms);

int num = reader.ReadInt32();
string str = reader.ReadString();
double d = reader.ReadDouble();
bool b = reader.ReadBoolean();
byte by = reader.ReadByte();
byte[] bytes = reader.ReadBytes(3);

Custom Binary Serialization

public class Player
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public float Health { get; set; }
    public Vector3 Position { get; set; }

    public void WriteTo(BinaryWriter writer)
    {
        writer.Write(Id);
        writer.Write(Name);
        writer.Write(Health);
        writer.Write(Position.X);
        writer.Write(Position.Y);
        writer.Write(Position.Z);
    }

    public static Player ReadFrom(BinaryReader reader)
    {
        return new Player
        {
            Id = reader.ReadInt32(),
            Name = reader.ReadString(),
            Health = reader.ReadSingle(),
            Position = new Vector3(
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle())
        };
    }
}

// Usage
using var stream = File.Create("player.dat");
using var writer = new BinaryWriter(stream);
player.WriteTo(writer);

using var readStream = File.OpenRead("player.dat");
using var reader = new BinaryReader(readStream);
var loaded = Player.ReadFrom(reader);

Binary vs JSON

Binary Serialization

  • Size: Compact
  • Speed: Faster
  • Readability: Not human-readable
  • Debugging: Difficult
  • Use case: Performance-critical, network protocols

JSON Serialization

  • Size: Larger (text-based)
  • Speed: Slower
  • Readability: Human-readable
  • Debugging: Easy
  • Use case: APIs, config files, data exchange

When to Use Binary

Use binary serialization only when size/speed justify the debugging difficulty. For most cases, prefer JSON for interoperability and maintainability.

Aspect Binary JSON
Size Compact Larger (text)
Speed Faster Slower
Readability Not human-readable Human-readable
Debugging Difficult Easy
Schema evolution Manual versioning Flexible
Use case Performance-critical, protocols APIs, config, data exchange
// JSON alternative for most cases
byte[] jsonBytes = JsonSerializer.SerializeToUtf8Bytes(data);
var data = JsonSerializer.Deserialize<MyData>(jsonBytes);

Version History

Feature Version Significance
Console class .NET 1.0 Core console I/O
Environment class .NET 1.0 Environment access
BinaryWriter/Reader .NET 1.0 Binary serialization
Console.GetCursorPosition .NET 5 Tuple-returning method
TickCount64 .NET Core 3.0 64-bit uptime counter

Key Takeaways

Console.Error for diagnostics: Separate error stream allows redirecting stdout while keeping errors visible.

Reset colors in finally: Always restore console colors to avoid corrupting the user’s terminal.

Environment variables are scoped: Process-level by default; User/Machine require Windows and appropriate permissions.

Use GetFolderPath for portability: Special folders resolve correctly across platforms.

Binary for performance: Use BinaryWriter/Reader for compact, fast serialization when human readability isn’t needed.

Prefer JSON for interoperability: Use binary only when size/speed justify the debugging difficulty.

Found this guide helpful? Share it with your team:

Share on LinkedIn