This episode teaches the C# language foundations: primitive data types, variable declaration, value and reference types, nullable, control flow, pattern matching, and functions and lambdas. All examples run directly in a console application.

Your first project ran successfully in episode 3. Now you enter the language that powers everything: C#. Episode 4 teaches the language foundations — data types, variables, control flow, functions, and lambdas. These are the tools you'll use in every line of .NET code from now on.
C# is a modern language that keeps evolving. Instead of memorizing syntax, focus on the concepts: value types versus reference types, nullable, and pattern matching. These three determine how you model data and write safe logic.
C# primitive types are aliases for types in the System namespace: int for integers, double for fractions, decimal for money, bool for booleans, and char and string for text. Example declarations:
int jumlah = 42;
double harga = 19.99;
decimal total = 99.99m;
bool aktif = true;
string nama = "Arman";decimal total = 99.99m uses the m suffix so the value is interpreted as decimal, not double. Choosing the right type matters: for money calculations, decimal avoids floating-point rounding errors.
The var keyword asks the compiler to infer the type from the value on the right:
var jumlah = 42;
var pesan = "Halo";var jumlah = 42 is inferred as int. var is not a dynamic type — the type is determined at compile time. Use it when the type is obvious from the right-hand side to keep code concise.
A value type stores its value directly on the stack — for example int, double, and struct. A reference type stores a reference to an object on the heap — for example class, string, and array. This difference determines the behavior during assignment and parameter passing.
An enum defines a set of named constants, and nullable indicates that a value can be empty:
enum StatusPembayaran { Pending, Sukses, Gagal }
StatusPembayaran status = StatusPembayaran.Sukses;
int? umur = null;int? umur = null marks a nullable type — the variable can hold a number or null. Since .NET 6, enabling Nullable makes the compiler warn when a reference type could be null, so null bugs are detected earlier.
C# control structures are similar to other modern languages:
if (status == StatusPembayaran.Sukses)
{
Console.WriteLine("Pembayaran diterima");
}
else
{
Console.WriteLine("Pembayaran gagal");
}
for (int i = 0; i < 3; i++)
{
Console.WriteLine(i);
}if, for, while, and foreach work as you already know. Curly braces must be opened and closed — and because top-level statements are used, this code can run directly in a single Program.cs file.
Modern C# replaces many if-else chains with pattern matching. Switch expressions map values concisely and type-safely:
string label = status switch
{
StatusPembayaran.Sukses => "OK",
StatusPembayaran.Gagal => "ERR",
_ => "PENDING"
};The switch expression above evaluates each pattern and returns a value. The _ (discard) pattern handles all other cases. Combining switch expressions with record types (episode 5) produces very expressive code.
Functions in C# are called methods. A method can be declared inside another method — this is called a local function:
int Tambah(int a, int b)
{
return a + b;
}
int LipatGanda(int nilai) => nilai * 2;
Console.WriteLine(Tambah(3, 4));
Console.WriteLine(LipatGanda(5));Tambah is a regular method with a block body, while LipatGanda uses an expression body (=>) for a one-line function. Both are valid; choose an expression body when the function body is concise enough.
A lambda is an anonymous function widely used with LINQ and collections. For example with List<T>.FindAll:
var angka = new List<int> { 1, 2, 3, 4, 5 };
var genap = angka.Where(x => x % 2 == 0).ToList();
foreach (var n in genap)
{
Console.WriteLine(n);
}angka.Where(x => x % 2 == 0) uses a lambda to filter even elements. Methods like Where and Select are the heart of LINQ, which we will use heavily for data access in episode 9.
Tip
Get into the habit of using foreach instead of index loops, and lambdas for collection transformations. This reduces bugs and makes the code's intent clearer.
Some pitfalls that happen often:
== compares values, not references — safe for strings.int and int? can't be mixed without a null check.switch expression must handle all possibilities or use the _ discard.Key takeaways:
decimal for money.var for inference; the type is determined at compile time.In the next episode 5 we will discuss OOP and core programming concepts — classes, properties, encapsulation, inheritance, polymorphism, interfaces, record types, and tuples. This is where you start modeling application domains with C#.