Side-by-side, interactive cheatsheets for C# programmers
comparing C# to other languages. Every example runs live in your browser — no
setup, no installation.
Choose your own path by reordering languages
What C# developers reach for when they want expressiveness over ceremony. Ruby drops static, explicit types, and boilerplate for a world where everything is an object, blocks replace delegates, and open classes let you add methods to Integer at runtime.
map, select, reduce, &:upcase replace LINQ's Select/Where/Aggregate without the lambda verbosity42.times, nil.nil?, true.class all work; no primitive/boxing distinctionString, Integer, or any class and add methods at runtime; no extension-method limitationinclude Comparable or Enumerable to gain 50+ methods for free, replacing C# interface boilerplateunless — puts value if valid? reads like plain English; no one-line if (cond) stmt; neededA familiar statically typed language — without the object-oriented ceremony. A C# developer reaches for Go when they want single-binary deployment, goroutines instead of async/await, and errors-as-values instead of exceptions — trading .NET's rich ecosystem for radical simplicity.
Task/async/await, no thread pool; concurrency is baked into the language(result, error) pairs make every failure explicit in the function signature.runtimeconfig.jsonNullReferenceException; every variable is initialized to a safe defaultStatic typing and the compiler are gone. A C# developer learning JavaScript trades explicit types, a single class-based object model, and a consistent this for a dynamic runtime with no safety net — but async/await syntax carries over almost unchanged.
var/let/const vs. C#'s var — a classic false friend: C#'s var is still statically typed, JavaScript's is not typed at allclass is sugar over a fundamentally different, mutable prototype-chain object modelthis is determined by the call site, not by where a method is defined — pulling a method off its object silently breaks it, unlike C#'s consistent instance this==/=== and coercion rules where C# has a single, non-coercing ==async/await syntax is nearly identical to C#'s, though JavaScript runs on one thread with an event loop instead of a thread poolmap/filter/reduce) mirror LINQ almost one to one — a C# developer's LINQ intuition transfers directlynull and undefined, where C#'s nullable reference types track a single nullPython is dynamically typed, interpreted, and indentation-driven — the near-opposite of C# in every technical decision, yet dominant in data science, machine learning, and scripting. The same async/await keywords, the same OOP concepts, but no compiler, no braces, and no type declarations.
name: str) are advisory, not enforced by the runtime[n*n for n in range(10) if n % 2 == 0]) replace LINQ's .Where(...).Select(...).ToList() in one readable expression@dataclass mirrors record: auto-generates constructor, equality, and repr from field annotations; add frozen=True for immutability*args/**kwargs, or isinstance() checks insteadTask/Thread — same async/await keywords, single-threaded event loop, no thread pool; asyncio.gather() is Task.WhenAll()The same industrial tier — static types, GC, LINQ-shaped pipelines, even the same out/in variance keywords — but the defaults all tighten. Null safety is enforced instead of advisory, classes are final unless opened, collections are read-only unless made mutable, and == is structural everywhere.
String? is a distinct type and dereferencing it without a check is a compile error — and !! actually throws at the assertion, unlike the erased !if, when, and try all return values — no ternary operator, no mutable placeholder before a try blocksealed + when gives compiler-proven exhaustiveness a C# switch over a class hierarchy cannot offer — no defensive _ arm== always calls equals (structural), with === for reference identity — inverting the C# default for classesWhere→filter, Select→map — but EAGER by default; asSequence() opts back into deferred pipelinesstatic: companion objects and object singletons; data class plays record (with copy() for with), and property delegation (by lazy, by interface forwarding) has no C# analogsuspend calls need no await keyword, return types stay unwrapped (String, not Task<string>), and scopes own their childrenMemory safety without a runtime — Rust gives C# developers zero-cost performance with compile-time guarantees. Where the .NET GC runs in the background, Rust's ownership model eliminates allocations entirely — no pauses, no null, no data races, and no exceptions.
IDisposablenull — absence is Option<T>; the compiler forces you to handle None before you can use the valueResult<T, E>; the ? operator propagates errors without try/catchShape::Circle(f64) variant is a true algebraic type; no sealed class hierarchy needed&str (borrowed slice) and String (owned buffer) — replacing C#'s single heap-allocated stringThe closest big-language cousin C# has — inference, properties, generics, async/await all rhyme — but the defaults invert. Value types are the culture (String, Array, Dictionary are all structs), optionals are enforced instead of advisory, enums carry payloads, and ARC replaces the garbage collector.
struct everywhere, copy-on-write collections, mutating visible in signatures — the aliasing bugs C# collections invite cannot happenString? is a real enum you must unwrap (if let, guard let) — C#'s erased nullable-reference warnings become compile errors, and dictionary lookups return optionals instead of throwingswitch — what C# fakes with an abstract record hierarchy and a defensive _ armInt — can conform to your protocol after the factthrows in the signature, try at the call, try? folding failures into optionals — C#'s invisible exception paths are syntactically impossibledeinit replaces the IDisposable/using apparatus — but retain cycles become your problem (weak, [weak self])async let and task groups instead of Task.WhenAll discipline, and actor makes data races a compile error where lock was always just conventionTypeScript is C#'s sibling, not its replacement — same designer (Anders Hejlsberg), same static typing instincts, but running on the JavaScript runtime with structural typing and no runtime type information.
implements declaration — the biggest conceptual shift from C#number type (IEEE 754 float) — no int, long, double, or decimal; use a library for financial arithmeticis MyInterface at runtime; use typeof, instanceof, and discriminant properties insteadnull (explicit absence) and undefined (not set) — always use ===, never ==string | number), literal types ("north" | "south"), and utility types (Partial<T>, Readonly<T>) replace C# enums, DTOs, and separate nullable variantsasync/await over a single event loop — no Task.Run, no threads, no ConfigureAwait(false)C#'s OOP cousin on a different runtime. Java and C# share syntax roots, generics, and a garbage-collected VM — but diverge on checked exceptions, generics implementation, async patterns, and the richness of their standard libraries.
== compares references for objects — always use .equals() for string and object value equality; the most common C# habit that breaks in Javafilter/map/collect) but requires explicit terminal operations and type erasure instead of reified genericsThe same .NET runtime, LINQ, and BCL you already use — with English keywords instead of braces. Visual Basic compiles to the identical CLR bytecode as C#, so List(Of T), async/await, and every NuGet package work exactly the same; what changes is syntax and a handful of surprising semantics like case-insensitivity and AndAlso/OrElse.
If...Then...End If, For Each...Next, and every other block closes with an explicit End keyword instead of { }myValue and MyValue are the same name; a real gotcha when translating C# code that relies on case to distinguish namesAndAlso / OrElse for short-circuit logic, plain And / Or for non-short-circuit — C#'s &&/|| vs &/| distinction, spelled out as wordsFrom x In list Where ... Select ...) as an alternative to the method-chain syntax C# developers default to — both compile to the same callsNothing instead of null, Me instead of this, MyBase instead of base — same concepts as C#, different vocabularyFunctional-first on the same .NET runtime — without the ceremony of classes. F# starts where C# leaves off: immutability by default, discriminated unions instead of sealed hierarchies, and exhaustive pattern matching enforced by the compiler.
let bindings cannot be reassigned; mutation requires the explicit mutable keyword, making side effects visibletype Shape = Circle of float | Rectangle of float * float and pattern-match exhaustively|> pipe operator threads data through transformations top-to-bottom, replacing LINQ method chains with readable functional pipelinesOption<'T> and Result<'T, 'E> replace null and exceptions for expected failure paths — no NullReferenceException, no hidden throwsGodot's built-in scripting language — Python-flavored, dynamically typed, no compilation step, and designed around Godot's Node tree. C# developers often use GDScript for rapid prototyping and C# for performance-critical systems within the same Godot project.
var x = 42 is a Variant; opt-in type annotations with var x: int = 42match replaces switch — matches ranges, arrays, dicts, and bind-patterns; far more powerful than C# switchsignal health_changed(new_health) is the observer pattern baked into the engine; no delegates requiredvar double = func(x): return x * 2 is explicit; invoke with double.call(5) rather than direct application:= for typed inference — var count := 42 infers int; without := the variable is untyped VariantEverything the runtime did for you, done by hand — and one thing it never could. Odin drops the garbage collector, classes, interfaces, LINQ, and async/await, then hands you Span<T> as the ordinary case, real discriminated unions, and the struct-of-arrays layout Unity built a second runtime to reach.
defer delete(...) beside each allocation, or an arena that drops a thousand of them at once — the bargain ArrayPool, stackalloc, and GC tuning were all approximating^T — so "is this a reference or a copy?" has one answer.Where().Select().Sum() chain becomes one loop with nothing allocated per stageunion is the discriminated union C# has wanted for five versions, with real exhaustiveness checking and no boxingIDisposable — errors are return values with or_return, and defer replaces both finally and using#soa expresses the DOTS layout change as one directive on the type