Skip to content

Instantly share code, notes, and snippets.

@tshego3
Created July 9, 2025 08:48
Show Gist options
  • Save tshego3/a31180a764bc5ebf7f3272eb63da601c to your computer and use it in GitHub Desktop.
Save tshego3/a31180a764bc5ebf7f3272eb63da601c to your computer and use it in GitHub Desktop.
C# Cheatsheet

C# Cheatsheet

Common Source Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

// Example 1: Combining collections using Concat (simulate spread operator)
class Example1
{
    public static void Run()
    {
        List<int> listA = new List<int> { 1, 2 };
        List<int> listB = new List<int> { 3, 4 };
        var combinedList = listA.Concat(listB).ToList();
        Console.WriteLine("Combined List using Concat (simulate spread operator): " + string.Join(", ", combinedList));
        // Output: 1, 2, 3, 4
    }
}

// Example 2: Updating a list using .Select with a return statement in a lambda block.
class Example2
{
    public static void Run()
    {
        List<int> numbers = new List<int> { 10, 20, 30 };
        var mappedList = numbers.Select(num =>
        // var mappedList = numbers.Select((num, index) =>
        {
            // Using block syntax with an explicit return statement to create an anonymous object.
            return new { Original = num, Double = num * 2 };
        }).ToList();
        Console.WriteLine("\nMapped List using Select with a return key:");
        foreach (var item in mappedList)
        {
            Console.WriteLine($"Original: {item.Original}, Double: {item.Double}");
        }
        // Output: Anonymous objects printed for each number.
    }
}

// Example 3: Chained syntax "Where().All().ToList()"
class Example3
{
    public static void Run()
    {
        // Wrap the list into our chainable type to allow a chainable All.
        List<int> numberList = new List<int> { 2, 4, 6, 8 };
        // Note: The original example's `.ToList().All()` would result in a boolean for `filteredList`.
        // If you intended to keep the list and then check 'All' as a separate step,
        // you would assign the ToList() result to a variable first.
        // For demonstration purposes, I'm showing the boolean result as implied by original chain.
        bool allEven = numberList
            .Where(n => n > 0) // Filter: keep numbers > 0
            .All(n => n % 2 == 0); // Check: if all numbers are even

        Console.WriteLine("\nResult of Where().All() chain: " + allEven);
        // Output: Result of Where().All() chain: True
    }
}

// Example 4: Using a lambda with fallback logic
class Example4
{
    public static void Run(object mappedList)
    {
        string result = new Func<string>(() =>
        {
            try
            {
                // This line was commented out in the original, but uncommented for demonstration
                // throw new Exception();
                // Assuming mappedList is accessible and not null, otherwise "Alternate value" will be used.
                // In a real scenario, you'd pass mappedList or define it within this scope.
                return (mappedList as List<object>)?.FirstOrDefault()?.ToString() ?? "Alternate value";
            }
            catch
            {
                return "Alternate value";
            }
        })();
        Console.WriteLine($"\n{result}");
        // Output: Either "{ Original = 10, Double = 20 }" or "Alternate value" based on the state and exception.
    }
}

// Example 5: Manually Creating the Logger
public class MyClassService
{
    private readonly ILogger<MyClassService> _logger;

    public MyClassService(ILogger<MyClassService> logger)
    {
        _logger = logger;
        _logger.LogInformation("MyClassService initialized.");
    }

    public void DoSomething()
    {
        _logger.LogInformation("Doing something important.");
    }
}

class Example5
{
    public static void Run()
    {
        var loggerFactory = LoggerFactory.Create(builder =>
        {
            // Configure logging providers
            builder.AddConsole();
            // Optionally add more providers here (e.g., Debug, EventSource)
        });

        ILogger<MyClassService> logger = loggerFactory.CreateLogger<MyClassService>();
        // ILogger<MyClassService> logger = new LoggerFactory().CreateLogger<MyClassService>(); // This would create a new factory every time

        // Now you can pass this logger to your service
        var myClassService = new MyClassService(logger);
        myClassService.DoSomething();
    }
}

class Program
{
    static void Main(string[] args)
    {
        try
        {
            Example1.Run();
            Example2.Run();
            // For Example 4, we need to pass a list (or simulate it)
            // Let's create a dummy list for Example 4 based on Example 2's output structure
            List<object> dummyMappedList = new List<object>
            {
                new { Original = 10, Double = 20 } // Simulating an item from mappedList
            };
            Example4.Run(dummyMappedList);
            Example3.Run(); // Changed order to make Example 3's output clearer after previous runs
            Example5.Run();
        }
        catch (Exception ex)
        {
            // 1. Catch the error
            Console.Error.WriteLine($"[LOG] An error occurred in PerformOperationWithLogging: {ex.Message}");
            // You might log more details, like ex.StackTrace, to a file or monitoring system here.

            // 2. Re-throw the original exception
            // IMPORTANT: Just 'throw;' preserves the original stack trace.
            // 'throw ex;' (BAD) would reset the stack trace to this point, losing origin info.
            throw;
        }

        Console.WriteLine("--- Demonstrating Alternatives and Why .Wait() Is Bad ---");

        var demo = new AsyncAlternatives();

        Console.WriteLine("\n--- 1. Fire and Forget (Ignoring the Task) ---");
        // Good for truly background, non-critical tasks where exceptions are handled internally.
        // Bad for: No completion notification, unobserved exceptions if not handled internally.
        demo.FireAndForgetCall();
        Console.WriteLine("Main thread continues immediately after FireAndForgetCall.");
        Task.Delay(500).Wait(); // Give async task a moment to start
        Console.WriteLine("--------------------------------------------\n");

        Console.WriteLine("--- 2. Task.Run() (Offloading to Thread Pool) ---");
        // Good for: Moving CPU-bound work off the main thread, fire-and-forget.
        // Bad for: Same unobserved exception issues as pure fire-and-forget, slight overhead.
        demo.TaskRunCall();
        Console.WriteLine("Main thread continues immediately after TaskRunCall.");
        Task.Delay(500).Wait(); // Give async task a moment to start
        Console.WriteLine("----------------------------------------------\n");

        Console.WriteLine("--- 3. ContinueWith (For Completion/Error Handling) ---");
        // Good for: Observing completion or handling exceptions from a fire-and-forget task without blocking.
        // Bad for: Can add complexity, less readable than async/await.
        demo.ContinueWithCall();
        Console.WriteLine("Main thread continues immediately after ContinueWithCall.");
        Task.Delay(3000).Wait(); // Give async task and continuation a moment to complete
        Console.WriteLine("-----------------------------------------------\n");

        Console.WriteLine("--- Why .Wait() Is Bad (and .Result too) ---");
        // .Wait() blocks the calling thread, leading to unresponsiveness and deadlocks.
        // It also wraps exceptions in AggregateException, making error handling clunky.
        try
        {
            Console.WriteLine("Attempting to use .Wait() - This will block the current thread.");
            demo.DoSomethingAsync().Wait(); // BAD: Blocks the Main thread
            Console.WriteLine("If this were a UI app, the UI would freeze here.");
            Console.WriteLine("If this were ASP.NET, a request thread would be tied up.");
        }
        catch (AggregateException ex)
        {
            Console.WriteLine($"Caught an AggregateException from .Wait(): {ex.InnerExceptions[0].Message}");
            // Better error handling: foreach (var inner in ex.InnerExceptions) { Console.WriteLine(inner.Message); }
        }
        // DEADLOCK POTENTIAL (common in UI/ASP.NET):
        // If DoSomethingAsync internally used await on a context that requires the current thread,
        // and that thread is blocked by .Wait(), it would deadlock.

        Console.WriteLine("\n--- Conclusion ---");
        Console.WriteLine("Prefer 'await' whenever possible to enable true asynchronous, non-blocking execution.");
        Console.WriteLine("Only use 'async void' for event handlers, with internal exception handling.");
        Console.WriteLine("For other 'void' methods, consider fire-and-forget with internal error handling,");
        Console.WriteLine("or 'ContinueWith' for observing completion/errors if not awaiting is an absolute must.");
    }
    
    class AsyncAlternatives
    {
        // A simulated async task for demonstrations
        public async Task DoSomethingAsync()
        {
            Console.WriteLine("    [Async] DoSomethingAsync started.");
            await Task.Delay(1000); // Simulate network call or I/O
            // Uncomment the line below to see exception handling in action for different scenarios
            // throw new InvalidOperationException("Simulated error in async task!");
            Console.WriteLine("    [Async] DoSomethingAsync finished.");
        }

        // 1. Fire and Forget
        public void FireAndForgetCall()
        {
            Console.WriteLine("    Calling DoSomethingAsync() and discarding the Task.");
            _ = DoSomethingAsync(); // Use '_' discard operator for clarity
        }

        // 2. Task.Run()
        public void TaskRunCall()
        {
            Console.WriteLine("    Wrapping DoSomethingAsync() in Task.Run().");
            _ = Task.Run(async () => await DoSomethingAsync());
        }

        // 3. ContinueWith
        public void ContinueWithCall()
        {
            Console.WriteLine("    Calling DoSomethingAsync() and attaching a ContinueWith.");
            DoSomethingAsync().ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    foreach (var ex in task.Exception.InnerExceptions)
                    {
                        Console.Error.WriteLine($"    [ContinueWith Error] Async task failed: {ex.Message}");
                    }
                }
                else if (task.IsCompletedSuccessfully)
                {
                    Console.WriteLine("    [ContinueWith Success] Async task completed successfully.");
                }
                // Add else if (task.IsCanceled) for cancellation handling
            }, TaskScheduler.Default); // Ensures continuation runs on a thread pool thread
        }
    }
}
Async OOP 3: Properties
// https://blog.stephencleary.com/2013/01/async-oop-3-properties.html
public sealed class MyClass
{
    private async Task<int> GetMyProperty()
    {
        await Task.Delay(100);
        return 13;
    }
    public Task<int> MyProperty
    {
        get { return GetMyProperty(); }
    }
}
public static async Task TestAsyncProperty()
{
    var t = new MyClass();
    var result = await t.MyProperty;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment