CSharp - Other

Use Enum Instead of Hard-Coded Numbers

Do

enum Months { 
  January = 1, 
  February = 2, 
  March = 3, 
  April = 4, 
  May = 5, 
  June = 6, 
  July = 7, 
  August = 8, 
  September = 9, 
  October = 10, 
  November = 11,
  December = 12 
} 

if((Months)month == Months.February) { 
  days = 28; 
}


Don't

if(month == 2) { 
  days = 28; 
}

Use GroupBy() with ToLookup() for Faster Lookups

GroupBy() is powerful but can be inefficient for multiple lookups. ToLookup() offers better performance for repeated accesses.


Problem:

var groups = numbers.GroupBy(n => n % 2);
var evens = groups.First(g => g.Key == 0);
//This iterates multiple times.


Solution:

var lookup = numbers.ToLookup(n => n % 2);
var evens = lookup[0];This avoids unnecessary iterations.

Use Null Coalescing Operator

Do

public Book GetTheBestBook(Book book) { 
  return book ?? new Book () { Name = "C# in Depth" }; 
}


Don't

public Book GetTheBestBook(Book book) { 
  if (book != null) { 
    return book; 
  } 
  else { 
    return new Book() { Name = "C# in Depth" }; 
  } 
}

Use String Interpolation

Do

public string GetTheBestBookName(Book book) { 
  return $"The Best book's Name is {book.Name}. and the author name is {book.Author}"; 
}


Don't

public string GetTheBestBookName(Book book) { 
  return "The Best book's Name is " + book.Name + " and the author name is " + book.Author; 
}

Use struct instead of class for value types that have a small memory footprint

Do

struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}


Don't

class Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

Use Ternary Operator Instead of If Else

Do

return age >= 18 ? "You are an adult" : "You are not an adult";


Don't

if (age >= 18) { 
  return "You are an adult"; 
} 
else { 
  return "You are not an adult"; 
}

Use the string.Format method instead of concatenating strings with the + operator

Do

string firstName = "John";
string lastName = "Doe";
string fullName = string.Format("{0} {1}", firstName, lastName);


Don't

string firstName = "John";
string lastName = "Doe";
string fullName = firstName + " " + lastName;

Use the StringBuilder class for concatenating large numbers of strings

Do

var builder = new StringBuilder();
foreach (var word in words)
{
    builder.Append(word).Append(' ');
}
string result = builder.ToString();


BEST

Span<char> buffer = stackalloc char[1024];
var pos = 0;

foreach (var word in words)
{
    word.AsSpan().CopyTo(buffer.Slice(pos));
    pos += word.Length;
    buffer[pos++] = ' ';
}

string result = new string(buffer.Slice(0, pos));


Don't

string result = "";
foreach (var word in words)
{
    result += word + " ";
}

Use the using statement to properly dispose of resources

Do

using (SqlConnection connection = new SqlConnection("connection string"))
{
    connection.Open();
    // Do something with the connection
}


Don't

SqlConnection connection = new SqlConnection("connection string");
connection.Open();
// Do something with the connection
connection.Close();

Using SemaphoreSlim to Limit API Calls Per Minute

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;


class Program
{
    private static readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1); // Allows one task at a time to update the counter
    private static readonly int MaxCallsPerMinute = 100;
    private static readonly TimeSpan RateLimitInterval = TimeSpan.FromMinutes(1);


    private static int CallsMade = 0;
    private static DateTime RateLimitReset = DateTime.UtcNow.Add(RateLimitInterval);


    static async Task Main(string[] args)
    {
        string apiUrl = "https://api.example.com/endpoint";


        // Simulate multiple API calls
        var tasks = new Task[200]; // Simulate 200 API requests


        for (int i = 0; i < tasks.Length; i++)
        {
            int requestId = i + 1;
            tasks[i] = MakeRateLimitedCall(apiUrl, requestId);
        }


        // Wait for all tasks to complete
        await Task.WhenAll(tasks);


        Console.WriteLine("All API calls completed.");
    }


    private static async Task MakeRateLimitedCall(string apiUrl, int requestId)
    {
        // Enforce rate-limiting logic
        await Semaphore.WaitAsync();
        try
        {
            await ApplyRateLimit(requestId);


            // Simulate API call
            using (HttpClient client = new HttpClient())
            {
                Console.WriteLine($"Request {requestId}: Started.");
                HttpResponseMessage response = await client.GetAsync(apiUrl);
                Console.WriteLine($"Request {requestId}: Completed with status {response.StatusCode}.");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Request {requestId}: Error - {ex.Message}");
        }
        finally
        {
            Semaphore.Release();
        }
    }


    private static Task ApplyRateLimit(int requestId)
    {
        lock (Semaphore)
        {
            if (DateTime.UtcNow >= RateLimitReset)
            {
                // Reset the counters when the time window is up
                CallsMade = 0;
                RateLimitReset = DateTime.UtcNow.Add(RateLimitInterval);
                Console.WriteLine($"Request {requestId}: Rate limit reset.");
            }


            if (CallsMade >= MaxCallsPerMinute)
            {
                var waitTime = (int)(RateLimitReset - DateTime.UtcNow).TotalMilliseconds;
                Console.WriteLine($"Request {requestId}: Rate limit reached, waiting {waitTime / 1000} seconds...");
                Monitor.Wait(Semaphore, waitTime);
            }


            CallsMade++;
        }


        return Task.CompletedTask;
    }
}


Using SemaphoreSlim to limit concurrent API calls

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;


class Program
{
    // Semaphore to limit concurrent calls
    private static readonly SemaphoreSlim Semaphore = new SemaphoreSlim(5); // Maximum 5 concurrent calls


    static async Task Main(string[] args)
    {
        string apiUrl = "https://api.example.com/endpoint";


        // Simulate multiple API calls
        var tasks = new Task[20]; // Simulate 20 API requests


        for (int i = 0; i < tasks.Length; i++)
        {
            int requestId = i + 1; // Capture the request ID
            tasks[i] = MakeApiCallAsync(apiUrl, requestId);
        }


        // Wait for all tasks to complete
        await Task.WhenAll(tasks);


        Console.WriteLine("All API calls completed.");
    }


    private static async Task MakeApiCallAsync(string apiUrl, int requestId)
    {
        await Semaphore.WaitAsync(); // Wait for a slot to be available


        try
        {
            using (HttpClient client = new HttpClient())
            {
                Console.WriteLine($"Request {requestId}: Started.");
                HttpResponseMessage response = await client.GetAsync(apiUrl);


                Console.WriteLine($"Request {requestId}: Completed with status {response.StatusCode}.");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Request {requestId}: Error - {ex.Message}");
        }
        finally
        {
            Semaphore.Release(); // Release the semaphore slot
        }
    }
}


Using System.Text.Json for Camel Case Serialization

Use JsonPropertyName Attribute for Camel Case Serialization

public class PersonWithAttributes
{
    [JsonPropertyName("firstName")]
    public string? FirstName { get; set; }
    
    [JsonPropertyName("surname")]
    public string? Surname { get; set; }
    
    [JsonPropertyName("age")]
    public int? Age { get; set; }
    
    [JsonPropertyName("isActive")]
    public bool? IsActive { get; set; }
}


Create JsonSerializerExtensions

public static class JsonSerializerExtensions
{
    public static string SerializeWithCamelCase<T>(this T data)
    {
        return JsonSerializer.Serialize(data, new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        });
    }

    public static T DeserializeFromCamelCase<T>(this string json)
    {
        return JsonSerializer.Deserialize<T>(json, new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        });
    }
}


How to use ?

var personWithAttributes = new PersonWithAttributes
{
    Age = 20,
    FirstName = "John",
    Surname = "Doe",
    IsActive = true
};

JsonSerializer.Serialize(personWithAttributes, new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});

or

personWithAttributes.SerializeWithCamelCase();

or

personWithAttributes.DeserializeFromCamelCase<PersonWithAttributes>()