LearnThatStack Ace your next interview

C# Programming.
Cheat sheet.

Quick reference for C# Programming - sectioned for fast scanning. Skim the part you're shaky on, walk in confident.

Backend Development 16-section reference ~11 min read

Summary

C# is a versatile, object-oriented programming language developed by Microsoft for building robust backend applications. This cheatsheet covers essential C# concepts for backend development including fundamentals, OOP principles, collections, async programming, ASP.NET Core, Entity Framework, design patterns, and best practices. Key topics include dependency injection, LINQ, Web API development, performance optimization, security, and testing strategies.

C# Fundamentals

Value Types vs Reference Types

// Value Types (stored on stack)
int x = 5;
int y = x; // y gets copy of x
x = 10;    // y is still 5

// Reference Types (stored on heap)
var list1 = new List<int> { 1, 2, 3 };
var list2 = list1; // both point to same object
list1.Add(4);      // list2 also has 4 elements

Nullable Types

int? nullableInt = null;
int value = nullableInt ?? 0; // null coalescing
int? result = nullableInt?.CompareTo(5); // null conditional

String Operations

// String interpolation
string name = "John";
string message = $"Hello, {name}!";

// StringBuilder for performance
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
    sb.Append(i);

Generics

public class Repository<T> where T : class
{
    public T GetById(int id) { /* implementation */ }
}

// Multiple constraints
public interface IService<T> where T : class, new()
{
    T Create();
}

Object-Oriented Programming

Inheritance & Polymorphism

public abstract class Animal
{
    public abstract void MakeSound();
    public virtual void Sleep() => Console.WriteLine("Sleeping");
}

public class Dog : Animal
{
    public override void MakeSound() => Console.WriteLine("Woof");
    public override void Sleep() => Console.WriteLine("Dog sleeping");
}

Interfaces

public interface IRepository<T>
{
    T GetById(int id);
    Task<T> GetByIdAsync(int id);
}

// Multiple interface implementation
public class UserRepository : IRepository<User>, IDisposable
{
    public User GetById(int id) { /* ... */ }
    public async Task<User> GetByIdAsync(int id) { /* ... */ }
    public void Dispose() { /* ... */ }
}

Properties & Encapsulation

public class Person
{
    // Auto-property with init-only setter (C# 9)
    public string Name { get; init; }
    
    // Backing field
    private int _age;
    public int Age 
    { 
        get => _age; 
        set => _age = value >= 0 ? value : 0; 
    }
    
    // Expression-bodied property
    public string Info => $"{Name}, {Age} years old";
}

Collections & LINQ

Common Collections

// List
List<int> numbers = new() { 1, 2, 3 };

// Dictionary
Dictionary<string, int> ages = new()
{
    ["John"] = 25,
    ["Jane"] = 30
};

// HashSet (unique values)
HashSet<int> uniqueNumbers = new() { 1, 2, 3, 3 }; // Contains 1,2,3

// Queue & Stack
Queue<string> queue = new();
queue.Enqueue("first");
string first = queue.Dequeue();

Stack<int> stack = new();
stack.Push(1);
int top = stack.Pop();

LINQ Queries

var users = new List<User>();

// Query syntax
var adults = from u in users
             where u.Age >= 18
             orderby u.Name
             select u;

// Method syntax (preferred)
var activeUsers = users
    .Where(u => u.IsActive)
    .OrderBy(u => u.CreatedAt)
    .Take(10)
    .ToList();

// Grouping
var usersByCountry = users
    .GroupBy(u => u.Country)
    .Select(g => new { Country = g.Key, Count = g.Count() });

// Join
var userOrders = users.Join(orders,
    u => u.Id,
    o => o.UserId,
    (u, o) => new { u.Name, o.Total });

Async Programming

async/await Pattern

public async Task<string> GetDataAsync()
{
    using var client = new HttpClient();
    var response = await client.GetAsync("https://api.example.com");
    return await response.Content.ReadAsStringAsync();
}

// Parallel async operations
public async Task<(string, string)> GetMultipleDataAsync()
{
    var task1 = GetDataAsync();
    var task2 = GetDataAsync();
    
    var results = await Task.WhenAll(task1, task2);
    return (results[0], results[1]);
}

ConfigureAwait

// In library code, avoid capturing context
public async Task<T> GetAsync<T>()
{
    var data = await GetDataAsync().ConfigureAwait(false);
    return JsonSerializer.Deserialize<T>(data);
}

CancellationToken

public async Task LongRunningOperation(CancellationToken ct)
{
    for (int i = 0; i < 100; i++)
    {
        ct.ThrowIfCancellationRequested();
        await Task.Delay(100, ct);
    }
}

Exception Handling

Try-Catch-Finally

try
{
    var result = await ProcessDataAsync();
}
catch (ArgumentException ex)
{
    // Handle specific exception
    _logger.LogError(ex, "Invalid argument");
    throw; // Re-throw to preserve stack trace
}
catch (Exception ex)
{
    // Handle general exception
    _logger.LogError(ex, "Unexpected error");
    throw new ServiceException("Processing failed", ex);
}
finally
{
    // Cleanup code
}

Custom Exceptions

public class BusinessException : Exception
{
    public string ErrorCode { get; }
    
    public BusinessException(string message, string errorCode) 
        : base(message)
    {
        ErrorCode = errorCode;
    }
}

ASP.NET Core Basics

Program.cs (Minimal API)

var builder = WebApplication.CreateBuilder(args);

// Add services
builder.Services.AddControllers();
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
builder.Services.AddScoped<IUserService, UserService>();

var app = builder.Build();

// Configure pipeline
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

app.Run();

Middleware

public class LoggingMiddleware
{
    private readonly RequestDelegate _next;
    
    public LoggingMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    
    public async Task InvokeAsync(HttpContext context)
    {
        // Before
        var start = DateTime.UtcNow;
        
        await _next(context);
        
        // After
        var duration = DateTime.UtcNow - start;
        Console.WriteLine($"{context.Request.Path}: {duration.TotalMilliseconds}ms");
    }
}

Web API Development

Controller Example

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly IUserService _userService;
    
    public UsersController(IUserService userService)
    {
        _userService = userService;
    }
    
    [HttpGet]
    public async Task<ActionResult<IEnumerable<UserDto>>> GetAll()
    {
        var users = await _userService.GetAllAsync();
        return Ok(users);
    }
    
    [HttpGet("{id}")]
    public async Task<ActionResult<UserDto>> GetById(int id)
    {
        var user = await _userService.GetByIdAsync(id);
        if (user == null)
            return NotFound();
        return Ok(user);
    }
    
    [HttpPost]
    public async Task<ActionResult<UserDto>> Create(CreateUserDto dto)
    {
        var user = await _userService.CreateAsync(dto);
        return CreatedAtAction(nameof(GetById), new { id = user.Id }, user);
    }
}

Model Validation

public class CreateUserDto
{
    [Required]
    [StringLength(50, MinimumLength = 3)]
    public string Name { get; set; }
    
    [Required]
    [EmailAddress]
    public string Email { get; set; }
    
    [Range(18, 120)]
    public int Age { get; set; }
}

Action Filters

public class ValidationFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
    
    public void OnActionExecuted(ActionExecutedContext context) { }
}

Entity Framework Core

DbContext Configuration

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) 
        : base(options) { }
    
    public DbSet<User> Users { get; set; }
    public DbSet<Order> Orders { get; set; }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Fluent API configuration
        modelBuilder.Entity<User>()
            .HasKey(u => u.Id);
            
        modelBuilder.Entity<User>()
            .Property(u => u.Email)
            .IsRequired()
            .HasMaxLength(255);
            
        modelBuilder.Entity<User>()
            .HasIndex(u => u.Email)
            .IsUnique();
            
        // Relationships
        modelBuilder.Entity<Order>()
            .HasOne(o => o.User)
            .WithMany(u => u.Orders)
            .HasForeignKey(o => o.UserId);
    }
}

Repository Pattern

public interface IRepository<T> where T : class
{
    Task<T> GetByIdAsync(int id);
    Task<IEnumerable<T>> GetAllAsync();
    Task AddAsync(T entity);
    void Update(T entity);
    void Delete(T entity);
    Task SaveChangesAsync();
}

public class Repository<T> : IRepository<T> where T : class
{
    protected readonly AppDbContext _context;
    protected readonly DbSet<T> _dbSet;
    
    public Repository(AppDbContext context)
    {
        _context = context;
        _dbSet = context.Set<T>();
    }
    
    public async Task<T> GetByIdAsync(int id)
    {
        return await _dbSet.FindAsync(id);
    }
    
    public async Task<IEnumerable<T>> GetAllAsync()
    {
        return await _dbSet.ToListAsync();
    }
    
    public async Task AddAsync(T entity)
    {
        await _dbSet.AddAsync(entity);
    }
    
    public void Update(T entity)
    {
        _dbSet.Update(entity);
    }
    
    public void Delete(T entity)
    {
        _dbSet.Remove(entity);
    }
    
    public async Task SaveChangesAsync()
    {
        await _context.SaveChangesAsync();
    }
}

LINQ with EF Core

// Include related data
var usersWithOrders = await _context.Users
    .Include(u => u.Orders)
    .ThenInclude(o => o.OrderItems)
    .Where(u => u.IsActive)
    .ToListAsync();

// Projection
var userDtos = await _context.Users
    .Select(u => new UserDto
    {
        Id = u.Id,
        Name = u.Name,
        OrderCount = u.Orders.Count()
    })
    .ToListAsync();

// Raw SQL
var users = await _context.Users
    .FromSqlRaw("SELECT * FROM Users WHERE Age > {0}", 18)
    .ToListAsync();

Dependency Injection

Service Lifetimes

// Transient - new instance each time
builder.Services.AddTransient<IEmailService, EmailService>();

// Scoped - new instance per request
builder.Services.AddScoped<IUserService, UserService>();

// Singleton - single instance for app lifetime
builder.Services.AddSingleton<ICacheService, CacheService>();

Service Registration

// Interface-based
builder.Services.AddScoped<IUserRepository, UserRepository>();

// Implementation factory
builder.Services.AddScoped<IService>(provider =>
{
    var config = provider.GetRequiredService<IConfiguration>();
    return new Service(config["ApiKey"]);
});

// Multiple implementations
builder.Services.AddScoped<INotificationService, EmailNotificationService>();
builder.Services.AddScoped<INotificationService, SmsNotificationService>();

// Retrieve all implementations
public class NotificationManager
{
    private readonly IEnumerable<INotificationService> _services;
    
    public NotificationManager(IEnumerable<INotificationService> services)
    {
        _services = services;
    }
}

Design Patterns

Singleton Pattern

public sealed class Singleton
{
    private static readonly Lazy<Singleton> _instance = 
        new(() => new Singleton());
    
    public static Singleton Instance => _instance.Value;
    
    private Singleton() { }
}

Factory Pattern

public interface IPaymentProcessor
{
    Task<bool> ProcessPayment(decimal amount);
}

public class PaymentProcessorFactory
{
    public IPaymentProcessor CreateProcessor(PaymentType type)
    {
        return type switch
        {
            PaymentType.CreditCard => new CreditCardProcessor(),
            PaymentType.PayPal => new PayPalProcessor(),
            _ => throw new NotSupportedException()
        };
    }
}

Generic Repository Pattern

public interface IUserRepository
{
    Task<User> GetByIdAsync(int id);
    Task<IEnumerable<User>> GetActiveUsersAsync();
    Task AddAsync(User user);
    Task UpdateAsync(User user);
}

public class UserRepository : IUserRepository
{
    private readonly AppDbContext _context;
    
    public UserRepository(AppDbContext context)
    {
        _context = context;
    }
    
    public async Task<User> GetByIdAsync(int id)
    {
        return await _context.Users
            .Include(u => u.Orders)
            .FirstOrDefaultAsync(u => u.Id == id);
    }
    
    public async Task<IEnumerable<User>> GetActiveUsersAsync()
    {
        return await _context.Users
            .Where(u => u.IsActive)
            .ToListAsync();
    }
}

Unit of Work Pattern

public interface IUnitOfWork : IDisposable
{
    IUserRepository Users { get; }
    IOrderRepository Orders { get; }
    Task<int> SaveChangesAsync();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly AppDbContext _context;
    
    public UnitOfWork(AppDbContext context)
    {
        _context = context;
        Users = new UserRepository(_context);
        Orders = new OrderRepository(_context);
    }
    
    public IUserRepository Users { get; }
    public IOrderRepository Orders { get; }
    
    public async Task<int> SaveChangesAsync()
    {
        return await _context.SaveChangesAsync();
    }
    
    public void Dispose()
    {
        _context.Dispose();
    }
}

Performance & Best Practices

Caching

public class CachedUserService : IUserService
{
    private readonly IUserService _userService;
    private readonly IMemoryCache _cache;
    
    public CachedUserService(IUserService userService, IMemoryCache cache)
    {
        _userService = userService;
        _cache = cache;
    }
    
    public async Task<User> GetByIdAsync(int id)
    {
        var cacheKey = $"user_{id}";
        
        if (_cache.TryGetValue(cacheKey, out User cachedUser))
            return cachedUser;
        
        var user = await _userService.GetByIdAsync(id);
        
        _cache.Set(cacheKey, user, TimeSpan.FromMinutes(5));
        
        return user;
    }
}

Async Best Practices

// ❌ Bad - blocking async code
var result = GetDataAsync().Result;
var data = GetDataAsync().GetAwaiter().GetResult();

// ✅ Good - proper async usage
var result = await GetDataAsync();

// ❌ Bad - async void (except event handlers)
public async void ProcessData() { }

// ✅ Good - return Task
public async Task ProcessData() { }

// ✅ Good - fire and forget with proper handling
_ = Task.Run(async () =>
{
    try
    {
        await LongRunningOperation();
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Background operation failed");
    }
});

String Performance

// ❌ Bad - string concatenation in loop
string result = "";
for (int i = 0; i < 1000; i++)
    result += i.ToString();

// ✅ Good - StringBuilder
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
    sb.Append(i);
var result = sb.ToString();

// ✅ Good - string.Join for collections
var numbers = Enumerable.Range(1, 1000);
var result = string.Join(",", numbers);

Security

Input Validation

public class UserInputValidator
{
    public bool ValidateEmail(string email)
    {
        if (string.IsNullOrWhiteSpace(email))
            return false;
            
        return Regex.IsMatch(email, 
            @"^[^@\s]+@[^@\s]+\.[^@\s]+$", 
            RegexOptions.IgnoreCase);
    }
    
    public string SanitizeInput(string input)
    {
        // Remove potentially dangerous characters
        return Regex.Replace(input, @"[<>""'%;()&+]", "");
    }
}

Authentication & Authorization

// JWT Bearer Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
        };
    });

// Authorization policies
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy => 
        policy.RequireRole("Admin"));
    
    options.AddPolicy("MinimumAge", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(18)));
});

// Controller with authorization
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class SecureController : ControllerBase
{
    [HttpGet("admin")]
    [Authorize(Policy = "AdminOnly")]
    public IActionResult AdminEndpoint()
    {
        return Ok("Admin access granted");
    }
}

Data Protection

public class EncryptionService
{
    private readonly IDataProtector _protector;
    
    public EncryptionService(IDataProtectionProvider provider)
    {
        _protector = provider.CreateProtector("MyApp.Purpose");
    }
    
    public string Encrypt(string plainText)
    {
        return _protector.Protect(plainText);
    }
    
    public string Decrypt(string cipherText)
    {
        return _protector.Unprotect(cipherText);
    }
}

Testing

Unit Testing with xUnit

public class UserServiceTests
{
    private readonly Mock<IUserRepository> _mockRepo;
    private readonly UserService _service;
    
    public UserServiceTests()
    {
        _mockRepo = new Mock<IUserRepository>();
        _service = new UserService(_mockRepo.Object);
    }
    
    [Fact]
    public async Task GetById_ReturnsUser_WhenUserExists()
    {
        // Arrange
        var expectedUser = new User { Id = 1, Name = "John" };
        _mockRepo.Setup(r => r.GetByIdAsync(1))
            .ReturnsAsync(expectedUser);
        
        // Act
        var result = await _service.GetByIdAsync(1);
        
        // Assert
        Assert.NotNull(result);
        Assert.Equal(expectedUser.Name, result.Name);
        _mockRepo.Verify(r => r.GetByIdAsync(1), Times.Once);
    }
    
    [Theory]
    [InlineData(0)]
    [InlineData(-1)]
    public async Task GetById_ThrowsException_WhenIdInvalid(int id)
    {
        // Assert
        await Assert.ThrowsAsync<ArgumentException>(() => 
            _service.GetByIdAsync(id));
    }
}

Integration Testing

public class UserControllerIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;
    
    public UserControllerIntegrationTests(WebApplicationFactory<Program> factory)
    {
        _factory = factory;
    }
    
    [Fact]
    public async Task GetUsers_ReturnsSuccessStatusCode()
    {
        // Arrange
        var client = _factory.CreateClient();
        
        // Act
        var response = await client.GetAsync("/api/users");
        
        // Assert
        response.EnsureSuccessStatusCode();
        Assert.Equal("application/json; charset=utf-8", 
            response.Content.Headers.ContentType.ToString());
    }
}

Test Doubles

// Mocking dependencies
var mockLogger = new Mock<ILogger<UserService>>();
mockLogger.Setup(l => l.LogInformation(It.IsAny<string>()));

// Stubbing data
var stubRepository = new Mock<IUserRepository>();
stubRepository.Setup(r => r.GetAllAsync())
    .ReturnsAsync(new List<User> 
    { 
        new User { Id = 1 }, 
        new User { Id = 2 } 
    });

// Verify interactions
mockLogger.Verify(l => 
    l.LogError(It.IsAny<Exception>(), It.IsAny<string>()), 
    Times.Never);

Key Concepts & Best Practices

Memory Management Essentials

Concept Description Example
Stack Value types, method parameters int x = 5
Heap Reference types, objects var list = new List<int>()
Garbage Collection Automatic cleanup, Gen 0/1/2 Managed automatically
IDisposable Unmanaged resources using var file = new FileStream()

SOLID Principles Summary

  • Single Responsibility: One class, one purpose
  • Open/Closed: Extend functionality without modifying existing code
  • Liskov Substitution: Derived classes must be substitutable for base classes
  • Interface Segregation: Multiple specific interfaces over one large interface
  • Dependency Inversion: Depend on abstractions, not implementations

Performance Optimization Strategies

  • String Operations: Use StringBuilder for concatenation, string.Join() for collections
  • Async Operations: Use async/await for I/O, avoid blocking with .Result
  • Database: Use projections, eager loading with Include(), pagination
  • Caching: Implement response caching, in-memory caching for frequently accessed data
  • Collections: Use appropriate collection types (HashSet for uniqueness, Dictionary for lookups)

Common Pitfalls to Avoid

Pitfall Problem Solution
Resource Leaks Not disposing IDisposable Use using statements
Blocking Async .Result or .Wait() Use await consistently
Null References Unhandled null values Use null checks, nullable types
Thread Safety Race conditions Use locks, concurrent collections
Hard-coded Values Configuration in code Use appsettings.json, IOptions

Development Best Practices

✅ Architecture: Use dependency injection, layered architecture
✅ Testing: Write unit tests, integration tests, use mocking
✅ Error Handling: Implement global exception handling, proper logging
✅ Security: Validate input, use authentication/authorization, encrypt sensitive data
✅ Code Quality: Follow naming conventions, keep methods focused, document complex logic
✅ Performance: Use appropriate data structures, implement caching, optimize database queries

Quick Reference

Common Data Types

// Value Types
int, long, float, double, decimal, bool, char, DateTime, Guid
struct MyStruct { }

// Reference Types  
string, object, class, interface, delegate, array
List<T>, Dictionary<TKey, TValue>, HashSet<T>

// Nullable Types
int? nullableInt = null;
string? nullableString = null; // C# 8+

Essential Attributes

// Web API
[ApiController], [Route], [HttpGet], [HttpPost]
[FromBody], [FromQuery], [FromRoute], [FromHeader]

// Validation
[Required], [StringLength], [Range], [EmailAddress]
[RegularExpression], [Compare]

// Entity Framework
[Key], [Required], [MaxLength], [Column], [Table]
[ForeignKey], [Index]

HTTP Status Codes Reference

  • 200 OK: Success
  • 201 Created: Resource created
  • 204 No Content: Success, no response body
  • 400 Bad Request: Invalid input
  • 401 Unauthorized: Authentication required
  • 403 Forbidden: Access denied
  • 404 Not Found: Resource not found
  • 500 Internal Server Error: Server error
Found this useful? Pass it on.
Pro · $10/mo

The sheet is free. Pro goes deeper.

Pro opens the full question library behind every sheet, every refresher and a monthly AI allowance. One subscription, all formats.

Full question library All refreshers Cancel anytime