Skip to main content

C# Basics


From Application Code to Native Machine Code in .NET

"C# (or other .NET languages) → IL → CLR → Native machine code"

  • You write application code in C#, F#, or VB.NET.
  • The code is compiled by the language compiler into IL (Intermediate Language), a CPU-independent set of instructions.
  • The CLR (Common Language Runtime) executes IL at runtime. It uses the JIT (Just-In-Time) compiler to translate IL into native machine code, optimized for your platform.
  • The result: your .NET application runs as fast, optimized native code on your OS.

Interview line: "A .NET program is compiled from source code to IL, which is then JIT-compiled by the CLR into native machine code for execution."


CLR (Common Language Runtime)

"CLR = runtime that executes .NET code"

  • JIT (Just-In-Time) — compiles IL to native code at runtime

  • Garbage collection — manages heap memory

  • Type system — enforces type safety

  • Assembly loading — loads and runs .NET assemblies

    Interview line: "CLR is the execution engine. It JIT-compiles IL, runs GC, and enforces the type system."


Input and Output

Console I/O

// Input
string line = Console.ReadLine();
int n = int.Parse(Console.ReadLine());

// Output
Console.WriteLine("Hello");
Console.Write("No newline");
Console.WriteLine($"Value: {n}");

File I/O

// Read
string text = File.ReadAllText("file.txt");
string[] lines = File.ReadAllLines("file.txt");

// Write
File.WriteAllText("file.txt", "content");
File.WriteAllLines("file.txt", lines);

// Async
await File.ReadAllTextAsync("file.txt");

Variables: Value vs Reference Types

"Value = copy on assign; Reference = same object"

TypeStoredExamples
ValueStack (usually)int, bool, struct, enum
ReferenceHeap, variable on stack holds referenceclass, string, array, delegate

Primitive / built-in value types

  • int, long, short, byte
  • float, double, decimal
  • bool, char

enum

public enum Status { Pending, Active, Done }
Status s = Status.Active;
int i = (int)s; // 1

struct (value type)

public struct Point
{
public int X, Y;
}
Point p; // stack; default 0,0

Nullable value types

int? n = null;
bool hasValue = n.HasValue;
int value = n ?? 0;

Interview line: "Value types live on the stack (or inside objects on the heap). Reference types live on the heap; the variable holds a reference."


Conversion: Explicit, Implicit, Parse, TryParse

Implicit (no cast, safe)

int i = 42;
long l = i;

Explicit (cast, may lose data)

double d = 3.14;
int i = (int)d; // 3

Parse (throws on failure)

int i = int.Parse("123");
// throws FormatException if invalid

TryParse (safe, returns bool)

if (int.TryParse("123", out int result))
Console.WriteLine(result);

Convert class

int i = Convert.ToInt32("42");
// throws on null or invalid

Interview line: "Use TryParse for user input; use Parse when you expect valid data. Convert offers various conversions but throws on failure."


Operations

  • Arithmetic: +, -, *, /, %
  • Comparison: ==, !=, <, >, <=, >=
  • Logical: &&, ||, !
  • Bitwise: &, |, ^, ~, <<, >>
  • Assignment: =, +=, -=, etc.
  • Null coalescing: ??, ??=
int a = 10, b = 3;
int q = a / b; // 3 (integer division)
double d = 10.0 / 3; // 3.333...
string s = name ?? "default";

Conditions and Loops

if / else

if (x > 0) {
} else if (x < 0) {
} else {
}

switch

switch (value)
{
case 1: break;
case 2: break;
default: break;
}
// switch expression (C# 8+)
var result = value switch { 1 => "One", 2 => "Two", _ => "Other" };

for / while / do-while

for (int i = 0; i < 10; i++) { }
while (condition) { }
do { } while (condition);

break / continue

  • break — exit loop or switch
  • continue — skip to next iteration

this and Extension Methods

this (current instance)

public void SetName(string name) { this.Name = name; }

Extension methods

"Add methods to existing types without subclassing"

public static class StringExtensions
{
public static bool IsNullOrEmpty(this string s) => string.IsNullOrEmpty(s);
}
// Usage: "".IsNullOrEmpty()
  • Static class, static method
  • First parameter: this TypeName name

Fields vs Properties

FieldsProperties
Direct storageGet/set accessors
public int x;public int X { get; set; }
Avoid public fieldsPreferred for public API
private int _age;
public int Age
{
get => _age;
set => _age = value &gt; 0 ? value : 0;
}
// Auto-implemented
public string Name { get; set; }

Access Modifiers

For types (class, struct, etc.)

  • public — any assembly
  • internal — same assembly only (default for non-nested types)

For members

  • public — everywhere
  • private — same class only (default for members)
  • protected — same class + derived
  • internal — same assembly
  • protected internal — same assembly OR derived (any assembly)

static, const, readonly

KeywordMeaningWhen set
staticOne per type, not per instanceN/A
constCompile-time constantAt declaration
readonlySet once (field)Constructor or declaration
public static int Count;
public const int Max = 100;
public readonly DateTime Created = DateTime.UtcNow;
  • static — shared by all instances; use for utilities, singletons
  • const — value type or string only; inlined by compiler
  • readonly — can be set in constructor; reference types: reference fixed, object can change

Overload vs Override

OverloadOverride
Same method name, different parameters (same class)Replace base method in derived class
Compile-timeRuntime (polymorphism)
No keywordoverride + base has virtual/abstract
// Overload
void Print(int x) { }
void Print(string s) { }

// Override
public override string ToString() => $"{Name}";

Parameter Modifiers: ref, out, in, params

C# provides special parameter modifiers that affect how arguments are passed between the caller and the method. These are especially useful for working with multiple return values, performance, or flexible argument lists.

ModifierMeaningUsage
refPass by reference. Must be initialized before.Can read/write in method. Caller sees changes.
outPass by reference. Must be assigned inside method (no need to init caller).Caller receives value from method.
inPass by reference as readonly (C# 7.2+).For large structs when you want to avoid copying, but keep read-only.
paramsVariable number of arguments (array-like).Method can accept any number of parameters as an array.

Examples:

// ref example
void Increment(ref int x) { x++; }
int a = 2;
Increment(ref a); // a is now 3

// out example
bool TryParseInt(string s, out int result)
{
return int.TryParse(s, out result);
}
int n;
if (TryParseInt("42", out n)) { /* n==42 */ }

// in example (readonly ref)
void PrintLength(in string s) { Console.WriteLine(s.Length); }

// params example
int Sum(params int[] numbers) => numbers.Sum();
int total = Sum(1, 2, 3, 4); // total = 10

Tips:

  • Both ref and out arguments must be explicitly marked at both callsite and declaration.
  • Use out for "try to get value" or "multiple return"; use ref for bidirectional parameters.
  • in is not for reference types (their reference is readonly, but object can still change!).
  • params must be the last parameter in the method signature.
  • Prefer minimizing use of ref and out in everyday coding (consider returning tuples instead); but they're common in performance-critical code, interop, or legacy APIs.

namespace

"namespace = logical grouping; avoid name clashes"

namespace MyApp.Services
{
public class UserService { }
}
// Usage
using MyApp.Services;
// or
var s = new MyApp.Services.UserService();
  • global using (C# 10) — apply to whole project
  • File-scoped namespace (C# 10): namespace MyApp; (no braces for file)

🧠 Ultra-Short Cheat Sheet

CLR = runtime (JIT, GC, type system)
Value = stack; Reference = heap
TryParse for input; Parse when valid
Extension method = static + this
Override = virtual/abstract + override
const = compile-time; readonly = once in ctor