By the end of this session, you will be able to:
| Time | Topic | Type | Duration |
|---|---|---|---|
| 0:00 | Variables & data types | Theory + Demo | 25 min |
| 0:25 | Type conversion | Theory + Demo | 15 min |
| 0:40 | Conditions: if/else & switch | Theory + Demo | 20 min |
| 1:00 | Break | — | 5 min |
| 1:05 | Loops: for, while, foreach | Theory + Demo | 20 min |
| 1:25 | Arrays | Theory + Demo | 20 min |
| 1:45 | Exercises | Exercise | 15 min |
varStored directly on the stack. Each variable holds its own copy of the data.
| Type | Description | Example |
|---|---|---|
int | 32-bit integer | int age = 25; |
long | 64-bit integer | long pop = 8_000_000_000L; |
double | 64-bit floating point | double price = 9.99; |
decimal | 128-bit high-precision | decimal tax = 0.15m; |
bool | true or false | bool isActive = true; |
char | Single Unicode character | char grade = 'A'; |
decimal for money and financial calculations — double has floating-point rounding errors.
Reference types — stored on the heap; the variable holds a reference, not the value itself.
| Type | Description |
|---|---|
string | Sequence of characters |
object | Base of every .NET type |
var — the compiler infers the type. The type is still fixed at compile time.
var is not dynamic. You cannot change the type after assignment.
// Reference types
string name = "Alice";
object anything = 42;
// Type inference with var
var count = 10; // int
var price = 9.99m; // decimal
var label = "Hello"; // string
// This would be a compile error:
// var x = 1;
// x = "hello"; // cannot change type
// String interpolation — embed expressions with $""
string firstName = "Alice";
int year = 2025;
string message = $"Hello, {firstName}! Born in {year - 25}.";
// → "Hello, Alice! Born in 2000."
// Verbatim string — backslashes are treated literally, no escape needed
string path = @"C:\Users\Alice\Documents\notes.txt";
string multiLine = @"Line one
Line two
Line three";
$@"C:\Users\{userName}\files" gives interpolation inside a verbatim string.
Convert classParse and TryParseImplicit — safe, no data loss. The compiler does it automatically when the destination type can hold all values of the source type.
int x = 100;
long y = x; // int → long
double d = x; // int → double
Explicit cast — possible data loss. You must declare it with (type).
double pi = 3.14159;
int truncated = (int)pi;
// truncated = 3 — decimal part lost!
long to int will silently overflow if the value is too large. The compiler will not warn you.
Convert class — converts between unrelated types. Throws on failure.
int n = Convert.ToInt32("42");
bool b = Convert.ToBoolean("true");
double d = Convert.ToDouble("3.14");
// Parse — throws FormatException if the string is not a valid number
int a = int.Parse("100"); // fine
int b = int.Parse("abc"); // throws!
// TryParse — returns false instead of throwing; always prefer for user input
string userInput = Console.ReadLine();
if (int.TryParse(userInput, out int result))
{
Console.WriteLine($"You entered: {result}");
}
else
{
Console.WriteLine("That is not a valid integer.");
}
TryParse when handling any input you did not create yourself — user input, file reads, API responses.
if / else if / else? :switch statement & switch expressionMulti-branch with if
int score = 75;
if (score >= 90)
{
Console.WriteLine("A");
}
else if (score >= 75)
{
Console.WriteLine("B");
}
else
{
Console.WriteLine("C or below");
}
Ternary — single-expression two-branch
// condition ? valueIfTrue : valueIfFalse
string label = score >= 50 ? "Pass" : "Fail";
// Useful for assignments and return values
bool isAdult = age >= 18;
string message = isAdult
? "Access granted"
: "Access denied";
if statement instead.
Classic switch statement
string day = "Saturday";
switch (day)
{
case "Saturday":
case "Sunday":
Console.WriteLine("Weekend");
break;
case "Monday":
Console.WriteLine("Back to work");
break;
default:
Console.WriteLine("Weekday");
break;
}
switch expression (C# 8+) — returns a value
string type = day switch
{
"Saturday" or "Sunday" => "Weekend",
"Monday" => "Back to work",
_ => "Weekday"
};
// Works great for mapping enums too:
string label = status switch
{
TaskStatus.Pending => "Not started",
TaskStatus.Completed => "Done",
_ => "In progress"
};
break in a switch statement causes fall-through to the next case.
for, while, do-whileforeach for collectionsbreak and continuefor — when you know the number of iterations
// init ; condition ; update
for (int i = 0; i < 5; i++)
{
Console.WriteLine(i);
}
// Prints: 0 1 2 3 4
// Nested — multiplication table
for (int i = 1; i <= 3; i++)
for (int j = 1; j <= 3; j++)
Console.WriteLine($"{i}x{j}={i*j}");
while — check condition before each iteration
int count = 0;
while (count < 5)
{
Console.WriteLine(count);
count++;
}
// do-while — body runs at least once
int input;
do
{
Console.Write("Enter a positive number: ");
input = int.Parse(Console.ReadLine());
} while (input <= 0);
foreach — cleanest way to iterate over a collection
string[] names = { "Alice", "Bob", "Carol" };
foreach (string name in names)
{
Console.WriteLine(name);
}
// Works on any IEnumerable — arrays,
// lists, query results, and more.
foreach over index-based for whenever you do not need the index.
break and continue
for (int i = 0; i < 10; i++)
{
if (i == 3) continue; // skip 3
if (i == 7) break; // stop at 7
Console.WriteLine(i);
}
// Prints: 0 1 2 4 5 6
break — exits the loop immediatelycontinue — skips to the next iterationLength propertyArray.Sort, Reverse, IndexOfDeclaration and initialization
// Fixed size, zero-initialized
int[] scores = new int[5];
// → [0, 0, 0, 0, 0]
// Initialize with values
int[] primes = { 2, 3, 5, 7, 11 };
// Equivalent long form
string[] names = new string[] { "Alice", "Bob" };
// Access by index (zero-based)
Console.WriteLine(primes[0]); // 2
primes[2] = 99; // replace 5 with 99
Console.WriteLine(primes.Length); // 5
Common operations
int[] numbers = { 5, 2, 8, 1, 9 };
Array.Sort(numbers);
// → [1, 2, 5, 8, 9]
Array.Reverse(numbers);
// → [9, 8, 5, 2, 1]
int pos = Array.IndexOf(numbers, 5);
// → 2 (index of value 5)
// Iterate with foreach
foreach (int n in numbers)
Console.Write(n + " ");
arr[arr.Length] throws IndexOutOfRangeException. Valid indices are 0 to Length - 1.
// 2D array — rows × columns
int[,] grid = new int[3, 3];
grid[0, 0] = 1;
grid[1, 2] = 7;
// Initialize inline
int[,] matrix = {
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 }
};
Console.WriteLine(matrix[1, 1]); // 5 (row 1, column 1)
// Iterate with nested loops
for (int row = 0; row < matrix.GetLength(0); row++)
for (int col = 0; col < matrix.GetLength(1); col++)
Console.Write(matrix[row, col] + " ");
== to compare double or float. Use Math.Abs(a - b) < 0.0001 instead — floating-point arithmetic is imprecise.
break causes fall-through: execution continues into the next case block, producing unexpected results.
< Length or <= Length - 1. Mixed usage causes skipped or out-of-bounds elements.
arr[arr.Length] throws at runtime. Arrays are zero-based: valid indices are 0 to arr.Length - 1.
int, bool, double) live on the stack; reference types (string, objects) live on the heapTryParse — not Parse — whenever the input comes from outside your codeswitch statements and always return a valueforeach over index-based for when you do not need the index0 to Length - 1Session 04 — C# Methods, Strings & Exception Handling
StringBuildernull safely — nullable types, ??, ?.try / catch / finallyBuild a small C# console program that demonstrates your understanding of types, control flow, and arrays.
Acceptance criteria:
TryParse to handle invalid input gracefullyfor loopArray.Sortswitch expression to map the maximum value to a label: <10 = "Low", 10–99 = "Medium", ≥100 = "High"