SPEAKER NOTES 01
Press N to toggle
BACKEND 101

Intermediate
C#
& Basic Java

Types · OOP · LINQ · Async · JVM
A technical primer for backend fundamentals

24 slides 2 languages 1 session
public class Dog {
  public string Name { get; set; }
  public void Bark() =>
    Console.WriteLine("Woof!");
}
var dogs = list
  .Where(d => d.Age > 2)
  .OrderBy(d => d.Name)
  .ToList();
ROADMAP

What
We
Cover

24slides
2parts
03 – 05

Types & Memory

Value types, reference types, nullability, IDisposable

06 – 08

C# OOP

Properties, inheritance, interfaces, abstract classes

09 – 11

Generics & LINQ

List, Dictionary, LINQ pipelines, delegates, lambdas

12 – 14

Modern C#

Async/await, exceptions, records, pattern matching

15 – 19

Basic Java

JVM, types, OOP, collections, streams, generics

20 – 21

C# vs Java

Runtime, generics, async, properties — side by side

22 – 24

Activities

Quiz, concept match, language classifier

STUDENT EXERCISE

Coding Challenges

3 hands-on exercises — open on your own laptop, write code, run the tests

Open coding.html
PART 01 · SLIDES 04 – 14

Intermediate
C#

Types  ·  OOP  ·  LINQ  ·  Async  ·  Modern Features

C# 10C# 11.NET 8
C#
C# · TYPES04

Value Types vs
Reference Types

Value Types — Stored Directly

int, double, bool, char, struct, enum. Stored where declared — stack for locals, inline in arrays. Assignment creates an independent copy. Changing one copy does not change another.

Reference Types — Stored by Reference

class, string, array, interface. Variable holds a reference to a heap object. Assignment copies the reference — both variables point to the same object.

Nullable Types

int? is shorthand for Nullable<int>. Lets a value type represent the absence of a value. Check with .HasValue or use the null-coalescing operator ??.

String is Immutable

String is a reference type that behaves like a value type because it never changes after creation. All "modifications" create a new string. Use StringBuilder in loops.

C#
int a = 10;
int b = a;
b = 99;
Console.WriteLine(a); // 10 — unchanged
var list1 = new List<int> { 1, 2, 3 };
var list2 = list1;
list2.Add(4);
Console.WriteLine(list1.Count); // 4 — same object!
int? maybeAge = null;
int age = maybeAge ?? 18;
var sb = new StringBuilder();
for (int i = 0; i < 100; i++)
  sb.Append(i);
string result = sb.ToString();
C# · MEMORY & RESOURCES05

Memory &
IDisposable

Garbage Collector

The CLR GC automatically reclaims heap memory when objects have no more references. You never call delete or free. GC runs on a background thread with generational collection.

IDisposable — Deterministic Cleanup

For unmanaged resources: files, sockets, DB connections. Implement IDisposable and provide a Dispose method. The GC handles memory; Dispose handles everything else.

using Statement — Guaranteed Cleanup

using var x = ... calls x.Dispose() automatically when the variable goes out of scope, even if an exception is thrown. The C# equivalent of RAII.

struct vs class

struct = value type, stack or inline, copied by value, no inheritance, no GC. class = reference type, heap, reference semantics, supports inheritance. Use struct for small data-only types.

C#
using var reader =
  new StreamReader("data.txt");
string text = reader.ReadToEnd();
// Dispose called automatically here
class DbConnection : IDisposable {
  private bool disposed = false;
  public void Dispose() {
    if (!disposed) {
      CloseConnection();
      disposed = true;
    }
  }
}
struct Point {
  public int X, Y;
}
C# · OOP06

Properties &
Encapsulation

Auto-Implemented Properties

public string Name { get; set; } — the compiler generates the private backing field. No separate field declaration needed. The cleanest way to expose data.

Computed & Validated Properties

Write explicit get and set bodies when you need logic — validation in the setter, computed values in the getter. Validation runs transparently on every assignment.

init — Construction-Only

public string Name { get; init; } can be set during object initialisation but never after. Perfect for creating immutable or semi-immutable objects without constructors.

Expression Bodies

=> syntax for single-expression properties and methods. Cleaner than a full block for simple computed values.

C#
class Person {
  private int _age;
  public string Name { get; set; }
  public int Age {
    get => _age;
    set => _age = value > 0 ? value : 0;
  }
  public string Email { get; init; }
  public bool IsAdult => _age >= 18;
}
var p = new Person {
  Name = "Alice",
  Email = "alice@example.com",
  Age = 25
};
C# · OOP07

The Four OOP Pillars

Encapsulation

Bundle data and methods. Use private, public, protected, internal. Properties expose data cleanly with validation built in.

class Dog {
private int _age;
public int Age {
  get; set;
}}

Inheritance

class Lab : Dog. Single base class. Call base constructor with base(). Use override and virtual. Use sealed to prevent further subclassing.

class Lab : Dog {
  public Lab(s n)
    : base(n) {}
  void Fetch() {}
}

Polymorphism

Mark base method virtual. Derived uses override. The compiler enforces correct signatures. Abstract forces derived classes to implement.

virtual void Speak();

override void
  Speak() {
  /*Dog speaks*/
}

Abstraction

interface defines a contract. A class can implement multiple interfaces. abstract class provides partial implementation with shared state.

interface IShape {
  double Area();
}
class Circle
  : IShape { }
C# · OOP08

Interfaces &
Abstract Classes

interface — Pure Contract

Defines what, not how. No fields, no constructors. A class can implement multiple interfaces. The foundation of testable, loosely-coupled code in C#.

abstract class

Can have fields, constructors, concrete methods, and abstract methods. Use when subclasses share real implementation. Cannot be instantiated directly.

sealed — Stop Inheritance

sealed class cannot be inherited from. sealed override prevents further overriding. Use when a class is a concrete final implementation.

Dependency Injection Pattern

Accept an interface in the constructor instead of a concrete class. Enables swapping real implementations for test fakes. Standard practice in all C# backend frameworks.

C#
interface IAnimalService {
  string GetSound(string animal);
  IEnumerable<string> GetAll();
}
abstract class Animal {
  public string Name { get; }
  protected Animal(string name) =>
    Name = name;
  public abstract void Speak();
}
sealed class Dog : Animal {
  public Dog(string n) : base(n) {}
  public override void Speak() =>
    Console.WriteLine("Woof");
}
C# · GENERICS & COLLECTIONS09

Generics &
Collections

No Type Erasure

Unlike Java, C# generics retain full type information at runtime. typeof(T) works inside generic methods. The JIT generates specialised code per value type — real performance benefit.

Constraints — where T :

where T : class — reference type only.
where T : struct — value type only.
where T : new() — must have parameterless constructor.
where T : IComparable — must implement interface.

Core Generic Collections

List<T> — dynamic array. Dictionary<K,V> — hash map. HashSet<T> — unique elements. Queue<T> and Stack<T> for ordered access.

IEnumerable<T>

The base interface for all sequences. Anything that implements it gets all LINQ operators. Your own types can implement it to participate in LINQ pipelines.

C#
static T Max<T>(T a, T b)
  where T : IComparable<T>
=> a.CompareTo(b) >= 0 ? a : b;
Max(3, 7);
Max("apple", "mango");
var scores = new Dictionary<string, int> {
  ["Alice"] = 95,
  ["Bob"] = 82
};
var names = new List<string>
  { "Alice", "Bob", "Charlie" };
var unique = new HashSet<int> { 1, 2, 3 };
C# · LINQ10

LINQ

Language Integrated Query

A set of extension methods on IEnumerable that brings SQL-style querying to any collection in C#. The C# equivalent of Java Streams — same concepts, different syntax.

Method Syntax — Most Common

Chain extension methods: Where (filter), Select (map/transform), OrderBy (sort), GroupBy, Aggregate. Returns IEnumerable until a terminal is called.

Query Syntax — SQL Style

from x in list where x.Age > 18 select x.Name — compiles to the same method calls. Some developers prefer it for complex multi-collection queries.

Deferred Execution

LINQ queries are lazy — they do not execute until you call a terminal operator like ToList(), First(), Count(), or iterate with foreach.

C#
var names = new List<string>
  { "Alice", "Bob", "Anna", "Charlie" };
var aNames = names
  .Where(n => n.StartsWith("A"))
  .Select(n => n.ToUpper())
  .OrderBy(n => n)
  .ToList();
int sum = Enumerable.Range(1, 100)
  .Sum();
var grouped = names
  .GroupBy(n => n[0])
  .ToDictionary(g => g.Key,
              g => g.ToList());
string first = names.First(n => n.Length > 4);
C# · DELEGATES & LAMBDAS11

Delegates,
Lambdas & Events

Delegate — Type-Safe Function Pointer

A type that represents a method signature. Variables of delegate type can hold any method with matching parameters and return type. The foundation of events and LINQ.

Func, Action, Predicate

Func<T,R> — takes T, returns R. Action<T> — takes T, returns void. Predicate<T> — takes T, returns bool. Cover 95% of use cases without defining custom delegates.

Lambda Expressions

x => x * 2 single expression. (x, y) => x + y multiple params. x => { ... } statement body. Closures capture outer variables by reference.

Events

Delegate fields declared with event. Subscribers add handlers with +=, remove with -=. Only the declaring class can invoke. Observer pattern built into the language.

C#
Func<int, int> square = x => x * x;
Action<string> print = Console.WriteLine;
Predicate<int> isEven = n => n % 2 == 0;
square(5);
print("hello");
isEven(4);
int threshold = 10;
Func<int, bool> above =
  x => x > threshold;
class Button {
  public event Action<string> Clicked;
  public void Click() =>
    Clicked?.Invoke("btn");
}
btn.Clicked += msg => Console.WriteLine(msg);
C# · ASYNC / AWAIT12

Async & Await

Why Async?

A thread blocked on a database query is wasted. With async/await, the thread is returned to the thread pool during I/O and resumed when the response arrives. Scales backend services dramatically.

Task — The Future Value

Task represents an ongoing operation. Task<T> represents a future value of type T. Equivalent to JavaScript's Promise or Java's CompletableFuture.

await — Suspend Without Blocking

Suspends the current method and returns the thread to the caller. Resumes automatically when the awaited Task completes. Does not block any thread.

Never call .Result or .Wait()

Calling these in a synchronous context causes deadlocks in ASP.NET. Always use await. Never use async void — exceptions become unobservable. Use async Task.

C#
async Task<string> FetchUserAsync(int id)
{
  var user = await _db.Users
    .FindAsync(id);
  return user?.Name ?? "Unknown";
}
async Task<List<string>> GetNamesAsync()
{
  var response = await _http.GetAsync(url);
  var json = await response.Content
    .ReadAsStringAsync();
  return JsonSerializer.Deserialize
    <List<string>>(json);
}
var a = FetchUserAsync(1);
var b = FetchUserAsync(2);
await Task.WhenAll(a, b);
C# · EXCEPTIONS13

Exception Handling

catch when — Exception Filters

catch (Exception e) when (e.Message.Contains("timeout")) — the catch only activates if the condition is true. Stack does not unwind if filter fails — more efficient than catch-and-rethrow.

throw vs throw ex

Bare throw inside catch rethrows and preserves the original stack trace. throw ex resets the stack trace to the current line — you lose where the exception originated. Always use bare throw.

finally

Always runs — normal exit, exception, or return. With using handling resource cleanup in most cases, you need finally mainly for state resets that are not resource-related.

Custom Exceptions

Inherit from Exception. Pass a clear message to base(message). Add extra properties for context. Use specific types rather than raw Exception in catch blocks.

C#
class NotFoundException : Exception {
  public int Id { get; }
  public NotFoundException(int id)
    : base($"Item {id} not found") {
    Id = id;
  }
}
try {
  var user = GetUser(42);
} catch (NotFoundException e)
  when (e.Id > 0) {
  logger.Warn(e.Message);
} catch (Exception e) {
  logger.Error(e);
  throw;
} finally {
  CleanUpState();
}
C# · MODERN C#14

Records &
Pattern Matching

record — Value-Based Equality

A class where equality is by content, not reference. Automatically generates ToString, Equals, GetHashCode, and a copy constructor. Perfect for DTOs and immutable data.

with — Non-Destructive Mutation

var b = a with { Name = "Bob" } creates a copy of a with only the specified properties changed. All other properties are copied. The standard pattern for immutable updates.

Pattern Matching — is

if (obj is string s) — checks type AND binds the variable in one step. if (n is > 0 and < 100) — relational patterns. Cleaner and safer than explicit casting.

Switch Expression

Returns a value based on pattern matching. Compiler warns if cases are not exhaustive. Cleaner than switch statement for transformations.

C# 9+
record Point(int X, int Y);
var p1 = new Point(1, 2);
var p2 = p1 with { Y = 5 };
p1 == new Point(1, 2); // true
object obj = "hello";
if (obj is string s && s.Length > 3)
  Console.WriteLine(s.ToUpper());
string grade = score switch {
  >= 90 => "A",
  >= 75 => "B",
  >= 60 => "C",
  _ => "F"
};
PART 02 · SLIDES 16 – 20

Basic
Java

JVM  ·  OOP  ·  Collections  ·  Streams

Java 8Java 17Java 21
Java
JAVA · JVM16

JVM Architecture

1
Java Source
.java file — human-readable code
↓ javac compiler
2
Bytecode
.class file — platform-independent
↓ JVM Classloader
3
JVM Runtime
Interpreter + JIT compiler
↓ hot path compilation
4
Native Machine Code
Near-native speed on frequently run paths

Write Once, Run Anywhere

Bytecode is OS-agnostic. Same dot class file runs on Windows, Linux, macOS, Android. No recompilation per platform — the JVM handles the differences.

GC vs CLR GC

Both generational, both low-pause. CLR advantage: value types avoid GC pressure — an array of int in C# stores ints directly. In Java every element in a List of Integer is a boxed heap object.

Virtual Threads (Java 21)

Lightweight JVM-managed threads. Millions can exist concurrently. Makes blocking code scale like async code without the async syntax overhead. Game changer for backend services.

JAVA · OOP17

Java OOP

extends & implements

One parent class with extends. Multiple interfaces with implements. @Override annotation enforces correct overriding at compile time — same role as C#'s override keyword.

No Properties — Explicit Getters/Setters

Java has no property syntax. Encapsulation uses private fields with public getName() and setName() methods. More verbose than C# properties but the same concept.

final — Java's sealed/readonly

final class cannot be subclassed. final method cannot be overridden. final field cannot be reassigned after init. Equivalent to C#'s sealed and readonly.

Records (Java 16+)

Same concept as C# records — immutable data carriers with value-based equality, auto-generated equals, hashCode, toString. Both languages solved the same problem identically.

Java
abstract class Animal {
  private String name;
  protected Animal(String n) { name = n; }
  public String getName() { return name; }
  public abstract void speak();
}
class Dog extends Animal {
  public Dog(String n) { super(n); }
  @Override
  public void speak() {
    System.out.println("Woof");
  }
}
record Point(int x, int y) {}
JAVA · STREAMS & GENERICS18

Java Streams
& Generics

Streams — Java's LINQ

Added in Java 8. Same lazy pipeline: filter, map, reduce, collect. Concepts identical to LINQ, syntax different. Java Streams, C# LINQ — learn one and the other makes immediate sense.

Generics — Type Erasure

Unlike C#, Java generics erase type info at runtime. List<String> becomes List in bytecode. Cannot do new T() or instanceof T. Affects performance for value types — requires boxing.

Optional — Null Safety

Optional<T> is Java's answer to nullable reference types. Wraps a value that might be absent. Use orElse, map, ifPresent — never call get() without checking.

Collectors

Collectors.toList(), groupingBy(), joining() — the Java equivalent of LINQ's ToList, GroupBy, and string joins. Used as the terminal argument to collect().

Java 8+
List<String> names = List.of("Alice","Bob","Anna");
List<String> result = names.stream()
  .filter(n -> n.startsWith("A"))
  .map(String::toUpperCase)
  .sorted()
  .collect(Collectors.toList());
int sum = IntStream.rangeClosed(1,100)
  .sum();
Optional<String> maybe =
  Optional.ofNullable(getName());
String val = maybe.orElse("default");
JAVA · COLLECTIONS19

Java Collections

JavaC# EquivalentAccessOrdered?Notes
ArrayList<T>List<T>O(1)InsertionDynamic array — default sequence choice
LinkedList<T>LinkedList<T>O(n)InsertionFast insert/remove at known positions
HashMap<K,V>Dictionary<K,V>O(1) avgNoHash map — fastest key-value lookup
TreeMap<K,V>SortedDictionary<K,V>O(log n)SortedKeeps keys in sorted order
HashSet<T>HashSet<T>O(1) avgNoUnique elements, fast membership test
PriorityQueue<T>PriorityQueue<T,P>O(1) topHeapAlways retrieve smallest/largest first
Java List only holds reference types — List<int> is not allowed. Use List<Integer> and autoboxing. C# List<int> stores ints directly — no boxing.
Neither language's basic collections are thread-safe. Java: ConcurrentHashMap. C#: ConcurrentDictionary.
COMPARISON20

C# vs Java

C#
Java
Runtime
CLR — .NET 8, cross-platform
JVM — OpenJDK, cross-platform
Value Types
struct — no boxing, no GC pressure
Primitives only — collections require boxing
Generics
Full runtime type info — no erasure
Type erasure at runtime
Async
async/await — first-class language feature
CompletableFuture + virtual threads (Java 21)
Properties
Built-in property syntax
Manual getters and setters by convention
Best For
Windows ecosystem, game dev (Unity), Azure
Android, enterprise, big data, financial systems
KEY TAKEAWAYS

What You
Now Know

01

C# has value types and reference types — structs avoid heap allocation and GC pressure entirely

02

Properties give C# clean encapsulation with get/set syntax — more concise than Java's explicit methods

03

LINQ brings SQL-style querying to any collection — the same pipeline model as Java Streams but with C# syntax

04

async/await lets I/O-bound code scale without blocking threads — essential for backend services

05

C# generics preserve runtime type information — no type erasure unlike Java generics

06

C# and Java are closer to each other than any other major language pair — learn one and the other is very accessible

C#
+
Java

NEXT STEPS

Open coding.html on your laptop
C# in Depth — Jon Skeet
Effective Java — Joshua Bloch
Build a REST API in both languages
ACTIVITIES

Time to
Practice

Quiz  ·  Concept Match  ·  Language Sort  ·  coding.html

QuizMatchSortOpen coding.html
Go
ACTIVITY 01 · QUICK FIRE QUIZ23

Quick Fire Quiz

8 questions  ·  C# and Java fundamentals

ACTIVITY 02 · CONCEPT MATCH24

Concept Match

Click a term, then click its matching definition  ·  Find all 8 pairs