// C# source compiles to IL, which the CLR JIT-compiles to native code at runtime
public int Add(int a, int b) => a + b;
// Compiled IL (simplified): ldarg.1, ldarg.2, add, ret
Topics
31
.NET CLI, SDK & Project Structure (csproj)
.NET vs .NET Framework
API Versioning
ASP.NET Core Middleware & Request Pipeline
Assemblies & NuGet
Authentication & Authorization (Identity, JWT, OAuth)
Background Services
Blazor (Server & WebAssembly)
Caching (In-Memory, Distributed & Redis)
CI/CD, Publishing & Deployment
CLR & Runtime
Configuration & Options
CORS & Cross-Origin Resource Sharing
Dependency Injection
Diagnostics & Performance
Docker & Containerization
Entity Framework Core & Data Access
Generic Host
Global Exception Handling & Middleware
gRPC Services
Health Checks & Readiness/Liveness Probes
Logging
Microservices & Distributed Architecture Patterns
Minimal APIs
MVC & Razor Pages
Rate Limiting & Throttling
RESTful Web APIs & Controllers
Secrets Management & Configuration Providers (Key Vault, User Secrets)
SignalR & Real-Time Communication
Testing in .NET (xUnit, Integration & Unit Testing)
Worker Services & IHostedService
CLR & Runtime
19 questions found
The CLR is the execution engine of .NET, responsible for managing memory (allocation and garbage collection), just-in-time (JIT) compiling IL code to native machine code, enforcing type safety, handling exceptions, managing threads, and providing security sandboxing. Any .NET language (C#, F#, VB.NET) compiles down to a common Intermediate Language (IL) that the CLR executes uniformly.
Real-world example
Because the CLR provides a common execution model, a library written in F# can be seamlessly consumed by a C# application, since both compile to the same IL that the CLR executes identically regardless of source language.
.NET vs .NET Framework;Memory Management & Garbage Collection
What is JIT (Just-In-Time) compilation, and how does it differ from ahead-of-time (AOT) compilation?
IntermediateJIT compilation translates IL into native machine code at runtime, immediately before a method is first executed, allowing the CLR to optimize compiled code based on the actual runtime environment (CPU architecture, observed usage patterns) -- at the cost of a warm-up delay for the first call to each method. AOT compilation (like Native AOT) instead compiles everything to native code during the publish/build step, eliminating JIT overhead and warm-up time entirely, at the cost of losing some runtime-adaptive optimization opportunities and dynamic code generation capabilities.
// JIT: method compiled to native code the first time it's called
public void ProcessOrder() { ... } // IL until first invocation, then JIT-compiled
// Native AOT: entire app compiled to native code at publish time
dotnet publish -p:PublishAot=true
Real-world example
A CLI tool invoked thousands of times per day (like a git hook) benefits significantly from Native AOT's elimination of JIT warm-up, since JIT's per-invocation startup cost would otherwise dominate the tool's very short total runtime.
.NET CLI
SDK & Project Structure (csproj);Diagnostics & Performance
What is tiered compilation, and how does it balance fast startup with peak execution performance?
AdvancedTiered compilation JIT-compiles a method quickly with minimal optimization on its first call (Tier 0, fast to produce but slower to execute), then, if the method is called frequently enough (a 'hot' method), the CLR recompiles it in the background with full optimizations (Tier 1) and seamlessly swaps in the optimized version -- balancing fast application startup (avoiding expensive full optimization for rarely-called methods) against long-term throughput (ensuring frequently-executed hot paths eventually get maximally optimized code).
<PropertyGroup>
<TieredCompilation>true</TieredCompilation> <!-- default: true -->
<TieredPGO>true</TieredPGO> <!-- dynamic profile-guided optimization, default: true in modern .NET -->
</PropertyGroup>
Real-world example
A web application's startup time improved noticeably after upgrading to a .NET version with better tiered compilation defaults, since most request-handling code initially runs at Tier 0 speed, with only genuinely hot paths eventually promoted to fully optimized Tier 1 code.
Diagnostics & Performance;CLR & Runtime
How does the CLR's type system distinguish between value types and reference types, and where is each typically allocated?
IntermediateValue types (structs, primitives like int/bool/double) are typically allocated on the stack (or inline within a containing object/array) and copied by value when assigned or passed, offering fast allocation/deallocation with no garbage collection overhead. Reference types (classes) are allocated on the managed heap, with variables holding a reference (pointer) to the heap location, requiring garbage collection to reclaim memory once no references remain.
struct Point { public int X, Y; } // value type, typically stack-allocated
class Person { public string Name; } // reference type, heap-allocated
Point p1 = new Point { X = 1, Y = 2 };
Point p2 = p1; // COPY -- p2 is independent of p1
Person person1 = new Person { Name = "Alice" };
Person person2 = person1; // REFERENCE -- both point to the same object
Real-world example
A performance-sensitive game engine uses structs extensively for small, frequently-created data like 2D/3D vectors specifically to avoid garbage collection pressure that would result from allocating millions of small reference-type objects per frame.
Data Types & Structures;Memory Management & Garbage Collection
Boxing wraps a value type in a heap-allocated object so it can be treated as a reference type (e.g., assigning an int to an object variable or adding it to a non-generic collection), incurring a heap allocation and copy. Unboxing extracts the value type back out of the boxed object, requiring a type check and copy. Both operations add measurable overhead compared to working with the value type directly, which is why generic collections (List<int> instead of ArrayList) are strongly preferred in modern .NET to avoid unnecessary boxing.
int number = 42;
object boxed = number; // boxing: heap allocation occurs
int unboxed = (int)boxed; // unboxing: type check + copy
// Generics avoid boxing entirely:
List<int> numbers = new(); // no boxing when adding ints
numbers.Add(42);
Real-world example
A performance audit of a hot code path discovers millions of unnecessary boxing allocations from storing int values in an old ArrayList-based cache, and switching to a generic List<int> or Dictionary<int, T> eliminates the boxing overhead entirely.
Data Types & Structures;Diagnostics & Performance
What is the Common Type System (CTS), and how does it enable interoperability between different .NET languages?
IntermediateThe Common Type System defines a shared set of types and rules for how types are declared, used, and managed that every .NET language's compiler must map its own type system onto, ensuring a C# class, an F# record, and a VB.NET class all ultimately produce compatible, interoperable IL-level types that any other .NET language can consume seamlessly without translation layers.
// C# class
public class Point { public int X { get; set; } }
// F# can consume it directly since both compile to compatible CTS-conforming IL
let p = Point(X = 5)
Real-world example
A data science team writes core numerical algorithms in F# for its expressive functional syntax, while the surrounding web API and business logic are written in C#, with both languages interoperating seamlessly because they share the CTS.
.NET vs .NET Framework;Data Types & Structures
How does the CLR handle exceptions at a low level, and what is the performance implication of throwing exceptions frequently?
AdvancedWhen an exception is thrown, the CLR walks up the call stack searching for a matching catch handler, unwinding stack frames and running finally blocks along the way -- this stack-walking and unwinding process is comparatively expensive (orders of magnitude slower than a normal method return), which is why exceptions should be reserved for genuinely exceptional conditions rather than used for routine control flow, where return values or the Try-pattern (TryParse, TryGetValue) are much more performant alternatives.
// Expensive: using exceptions for expected, frequent control flow
try { return int.Parse(userInput); }
catch (FormatException) { return 0; } // exception thrown on every invalid input
// Better: TryParse avoids exception overhead entirely
return int.TryParse(userInput, out var result) ? result : 0;
Real-world example
A high-throughput data validation pipeline processing millions of potentially malformed records switches from try/catch-based parsing to TryParse-based validation, measurably improving throughput since exceptions were previously being thrown on a significant fraction of malformed records.
Exception Handling;Diagnostics & Performance
What is the relationship between the CLR and the .NET class library (BCL - Base Class Library)?
IntermediateThe CLR is the execution engine (memory management, JIT, type system), while the Base Class Library is the vast collection of pre-built types and APIs (collections, I/O, networking, LINQ, string manipulation) that applications actually call -- the BCL itself is written in C# (and some C++ for performance-critical low-level pieces) and runs on top of the CLR just like any application code, providing the foundational building blocks every .NET program relies on.
// BCL types used constantly, running on top of the CLR
List<string> names = new();
StreamReader reader = new StreamReader("file.txt");
var result = names.Where(n => n.StartsWith("A")).ToList(); // LINQ, part of BCL
Real-world example
Understanding this layering clarifies why upgrading the CLR/runtime version (e.g., .NET 6 to .NET 8) can bring both execution engine improvements (faster JIT) and BCL improvements (new APIs, optimized implementations of existing ones) simultaneously.
CLR & Runtime;Diagnostics & Performance
How does the CLR ensure type safety and memory safety, and what categories of bugs does this prevent compared to unmanaged languages like C++?
AdvancedThe CLR enforces type safety by verifying IL code (checking that operations are performed on compatible types) and manages all memory allocation/deallocation itself via garbage collection rather than manual pointer arithmetic, preventing entire categories of bugs common in unmanaged languages: buffer overflows, use-after-free, dangling pointers, double-free errors, and most memory corruption issues -- though it doesn't eliminate all bugs (like logical errors, deadlocks, or resource leaks from undisposed unmanaged resources).
// The CLR prevents this class of bug entirely (unlike C/C++):
int[] array = new int[5];
// array[10] = 1; // throws IndexOutOfRangeException, not silent memory corruption
// Type safety enforced at the IL level:
object obj = "hello";
// int x = (int)obj; // throws InvalidCastException, not undefined behavior
Real-world example
A security audit comparing a .NET service to a legacy C++ component notes that entire vulnerability classes (buffer overflows, use-after-free) that plagued the C++ code simply cannot occur in the managed .NET code due to the CLR's built-in safety guarantees.
Memory Management & Garbage Collection;Exception Handling
What does the `unsafe` keyword and pointer usage in C# allow, and why would a developer opt into it?
IntermediateThe `unsafe` keyword enables code blocks that use raw pointers, pointer arithmetic, and direct memory manipulation, bypassing the CLR's normal type-safety and bounds-checking guarantees -- used sparingly for performance-critical scenarios (like high-performance buffer manipulation, interop with native libraries, or certain SIMD operations) where the overhead of managed safety checks is measurably significant and the developer accepts the increased risk of memory-safety bugs in exchange for raw performance.
unsafe {
fixed (byte* ptr = buffer) {
for (int i = 0; i < buffer.Length; i++) {
*(ptr + i) = 0; // direct pointer manipulation, bypasses bounds checking
}
}
}
// Requires <AllowUnsafeBlocks>true</AllowUnsafeBlocks> in the .csproj
Real-world example
A high-performance image processing library uses unsafe pointer manipulation to iterate over pixel buffers directly, avoiding the bounds-checking overhead of normal array indexing in a hot loop processing millions of pixels per second.
Memory Management & Garbage Collection;Diagnostics & Performance
Showing 1–10 of 19