Value and reference types
TrapA method edits only its own copy of a struct argument.
var gets a fixed type at compile time.await frees the thread while I/O runs. Blocking on .Result wastes that thread and can deadlock.TrapA method edits only its own copy of a struct argument.
decimal p = 19.99m;double can't store 0.1 exactly(int)3.9Needs a cast, drops the .9int.TryParse(s, out n)false on bad input, no throwTrapdecimal and double don't mix without an explicit cast.
const int Max = 100;Static, primitives and stringsreadonly DateTime _at;var n = 42;Typed at compile time, unlike dynamicTrapThe compiler copies a const into every caller, so other assemblies keep the old value until rebuilt.
int? n = null;n ?? 0#nullable enableCompiler warnings onlyTrapReading .Value on an empty int? throws Invalid. Check HasValue first.
new StringBuilder()"""{"a": 1}""""Hello"u8Encoded at compile timeTrapEach += on a string copies all the text so far. In a loop, the work grows with the square of the length.
HashCode.Combine(X, Y)string.Intern(s) TrapTwo equal strings in object variables compare by reference with ==. Call Equals instead.
protected internalprivate protectedTipC# covers the four OOP pillars with access modifiers, : Base, override, and abstract classes or interfaces.
static Config() { }class Svc(IRepo repo)required string NameTrapA primary constructor parameter isn't a field and isn't readonly. Any member can reassign it.
overrideChosen at run timenew void Print()sealed overrideTrapWith new, a Base variable holding a Derived still runs the base method. Only override is polymorphic.
abstract class AnimalInherit only oneinterface IFlyableImplement many. No instance fieldsTipPrefer composition for has-a. A Car holds an Engine field, and you can swap in another implementation.
record Point(int X, int Y);p with { X = 5 }(int Sum, int Count)new { u.Name, u.Age }Read-only, stuck in its methodobj[key]this[string key]operator +(V a, V b)partial class Personparams int[] xsthis string sIn a static classTrapAsync methods and iterators can't take ref, out or in.
obj is string sobj as stringobject o = 42;Allocates on the heapTrapA boxed int unboxes only to int. (long)o throws Invalid.
x switch { 1 => "a", _ => "b" }{ Age: >= 65 }> 0 and < 10[var a, .., var z]TrapA switch expression throws when no arm matches.
List<int>ArrayListBoxes values, no type checksDictionary<K, V>HashSet<T>TipArrays have a fixed size. Share a dictionary across threads with Concurrent.
where T : class, new()IProducer<out T>in T makes it contravariantwhere T : INumber<T>TipGenerics catch type errors at compile time and store value types without boxing.
yield return x;TrapCalling an iterator method runs none of its body. Each foreach runs it from the start.
Func<int, int>Action<string>op += Log;Returns the last method's resultdelegate { }event EventHandler Placed;Placed += OnPlaced;Placed -= OnPlaced;Placed?.Invoke(this, e)TrapAn event lets outside code only += and -=. A plain delegate field also lets it invoke or reset the list.
xs.Where(x => x > 5).Select(x => x * 2)Works on lists, EF Core and XMLfrom x in xs where x > 5 select xTipQuery syntax compiles to the same method calls, so neither is faster. Count exists only as a method.
Expression<Func<T, bool>>TrapToList() or AsEnumerable() before Where loads every row, then filters in memory.
.First()Take the first match.FirstOrDefault() Take the first, or default.Single()Demand exactly one.SingleOrDefault() Demand one or noneTipUse Single for a primary-key lookup, where a second match means bad data.
GroupBy(s => s.Grade)SelectMany(s => s.Items)join o in orders on c.Id equals o.CustomerIdTrapQuery syntax compares join keys with equals, not ==.
async Task<int> Get()async Task Save()Tipasync creates no thread. The compiler rewrites the method into a state machine.
.Result.Wait()Wastes a thread, can deadlockTrap.Result on a UI or classic ASP.NET thread can deadlock. The rest of the method needs that thread.
await Task.Delay(ms)Thread.Sleep blocks insteadawait Task.Run(Work).ConfigureAwait(false) await Task.WhenAll(ts) await Task.WhenAny(ts) cts.CancelAfter(5000) Code must check the tokenTrapWhenAny leaves the other tasks running. Pass them a Cancellation so you can stop them.
IAsyncEnumerable<T> await foreach (var o in xs)ValueTask<int>When it usually finishes synchronouslyTrapAwait a ValueTask only once. Call AsTask() first if you need WhenAll or a second await.
new Thread(Work)ThreadPool.QueueUserWorkItem No result or exceptions backlock (_gate) { }On a private object, or a .NET 9 LockInterlocked.Increment await sem.WaitAsync(); A lock body can't contain an awaitTipA Task adds what a pool job lacks. You can await it, read its result, see its exceptions and cancel it.
TrapNever lock on this or a string. Code outside your class can lock the same object.
Channel.CreateBounded<T>(100) Writers wait while it's fullreader.ReadAllAsync() TipUse Concurrent when threads only share a collection and never wait on it.
finally always runsthrow;throw new AppException(msg, ex);class OutOfStockException : ExceptionTrapthrow ex; resets the stack trace to the catch block, so the log loses the line that failed.
using var f = Open();A using block frees it soonerGC.SuppressFinalize(this); ~Holder()Or use a SafeHandleTrapA finalizer runs at no set time, and its object survives one extra collection.
GC.Collect()Almost neverWeakReference<T> ArrayPool<byte>.Shared TrapEvent handlers are the classic leak. A long-lived publisher keeps every subscriber alive until it unsubscribes.
arr.AsSpan()[2..8] stackalloc byte[256]ref struct Parserscoped Span<int> sTrapA Span<T> can't be a class field or live across an await. Store a Memory<T> there instead.
Asked in interviews
AskedExplainSpan<T> and Memory<T> and their use cases.GetCustomAttribute<A>() IIncrementalGenerator TrapReflection is much slower than a direct call, and a renamed member breaks it at run time, not in the build.
net10.0LTS until November 2028global using System.Linq;namespace App.Api;TrapOdd-numbered releases like .NET 9 get only two years of support. An LTS release gets three.
AddTransient<I, T>()AddScoped<I, T>()AddSingleton<I, T>()TrapA singleton holding a scoped DbContext is a captive dependency. Use IService instead.
app.MapGet("/users", GetAll).Include(o => o.Customer).Select(o => new { o.Id })db.SaveChangesAsync() DbContext is the unit of workTrapLazy loading in a loop runs one query per row. 50 rows become 51 queries, the N+1 problem.
Lazy<Config>In a static readonly fieldHttpUtility.HtmlEncode(s) Asked in interviews
AskedExplain security best practices in C#.Assert.That(x, Is.EqualTo(5));mock.Verify(m => m.Save(), Times.Once)WebApplicationFactory<Program> TipMeasure speed with BenchmarkDotNet. Stopwatch limits in unit tests make them flaky.
Asked in interviews
AskedWhat are common debugging and testing practices in C#?Nothing matches. Try a shorter word, or clear the search.
With Pro, you can:
Already have Pro? Sign in