C# Full Course: Core Concepts, a Worked Checkout Program and Exam Traps

Learn C# by tracing and building one complete checkout program, from typed values and collections to interfaces, validation, rounding and common assessment traps.

KnowledgeGate Team

Exam prep & CS education

Updated 28 Oct 20266 min read

A C# full course can become a disconnected tour of keywords. Recognising each keyword does not teach you to predict or build a program. This checkout connects types, control flow, collections, methods and objects, then validates input and traces the exact output. Use Coding & Skills when you want broader study context around the language.

1. Build the C# mental model before memorising syntax

A .cs source file is compiled into managed code. The .NET runtime loads that code, and execution enters the program. C# is the language; .NET is the platform and runtime ecosystem. They are related, but they are not interchangeable names.

In string learner = "Asha"; int solved = 7; Console.WriteLine($"{learner} solved {solved} problems");, each declaration gives a variable a static type, = assigns a value, $"..." creates an interpolated string, semicolons end statements, and Console.WriteLine is a method call. The output is Asha solved 7 problems.

Keep three failure classes separate. Misspelling Console is a compile-time error. Indexing beyond a list can fail at runtime. A wrong discount formula may compile and run but produce a logical error.

2. Types, operators and control flow with traceable values

Choose types by purpose: int for counts, decimal for these money values, bool for conditions, char for one character, string for text, and a class instance for a domain object. The suffix in 120m and 75.50m makes them decimal literals. Binary floating-point is unnecessary for this checkout.

Trace operators with actual values:

  • 7 / 2 uses two int operands, so the result is 3.

  • (double)7 / 2 produces 3.5.

  • From int x = 5, Console.WriteLine(x++ + ++x); prints 12 and leaves x == 7. The left operand is evaluated before the right operand in this C# expression.

For the checkout, accept a quantity only when quantity > 0 && quantity <= product.Stock. Apply a 10% discount when subtotal >= 500m; otherwise use zero. A foreach loop prints cart lines. Use break for deliberate exit and continue for deliberate skipping, not as default loop decoration.

3. Worked C# program: calculate Asha's checkout

The fixed data is: N101, Notebook, 120.00m, stock 5; and P205, Pen Set, 75.50m, stock 4. Asha requests three notebooks and two pen sets and pays 600.00m. The program applies a 10% discount from 500.00m, followed by an illustrative 18% tax. That tax is only a programming fixture, not tax advice or a real invoice rule.

csharp
using System;
using System.Collections.Generic;
using System.Linq;

record Product(string Code, string Name, decimal Price, int Stock);
record CartLine(Product Product, int Quantity)
{
    public decimal LineTotal => Product.Price * Quantity;
}

internal static class Program
{
    static void Main()
    {
        var catalog = new Dictionary<string, Product>
        {
            ["N101"] = new("N101", "Notebook", 120.00m, 5),
            ["P205"] = new("P205", "Pen Set", 75.50m, 4)
        };
        var cart = new List<CartLine>();

        try
        {
            cart.Add(AddLine(catalog, "N101", 3));
            cart.Add(AddLine(catalog, "P205", 2));
        }
        catch (Exception ex) when (ex is ArgumentException
            or KeyNotFoundException or InvalidOperationException)
        {
            Console.WriteLine(ex.Message);
            return;
        }

        foreach (CartLine line in cart)
            Console.WriteLine($"{line.Product.Name}: {line.Quantity} x " +
                $"{line.Product.Price:F2} = {line.LineTotal:F2}");

        decimal subtotal = cart.Sum(line => line.LineTotal);
        decimal discount = GetDiscount(subtotal);
        decimal discountedAmount = subtotal - discount;
        decimal tax = GetRoundedTax(discountedAmount);
        decimal total = discountedAmount + tax;
        decimal paid = 600.00m;

        Console.WriteLine($"Subtotal: {subtotal:F2}");
        Console.WriteLine($"Discount: {discount:F2}");
        Console.WriteLine($"Tax: {tax:F2}");
        Console.WriteLine($"Total: {total:F2}");
        Console.WriteLine($"Paid: {paid:F2}");
        Console.WriteLine($"Change: {paid - total:F2}");
    }

    static CartLine AddLine(
        Dictionary<string, Product> catalog, string code, int quantity)
    {
        if (quantity <= 0)
            throw new ArgumentOutOfRangeException(nameof(quantity));
        if (!catalog.TryGetValue(code, out Product? product))
            throw new KeyNotFoundException($"Unknown product code: {code}");
        if (quantity > product.Stock)
            throw new InvalidOperationException(
                $"Requested quantity {quantity} exceeds stock {product.Stock}");
        return new CartLine(product, quantity);
    }

    static decimal GetDiscount(decimal subtotal) =>
        subtotal >= 500.00m ? subtotal * 0.10m : 0m;

    static decimal GetRoundedTax(decimal discountedAmount) =>
        decimal.Round(discountedAmount * 0.18m, 2,
            MidpointRounding.AwayFromZero);
}

Now calculate independently of the code. Notebooks cost 3 x 120.00 = 360.00; pen sets cost 2 x 75.50 = 151.00. The subtotal is 360.00 + 151.00 = 511.00. Discount is 511.00 x 0.10 = 51.10, leaving 459.90. Raw tax is 459.90 x 0.18 = 82.782, rounded once to 82.78. Total is 459.90 + 82.78 = 542.68; change is 600.00 - 542.68 = 57.32.

The exact output is:

Code
Notebook: 3 x 120.00 = 360.00
Pen Set: 2 x 75.50 = 151.00
Subtotal: 511.00
Discount: 51.10
Tax: 82.78
Total: 542.68
Paid: 600.00
Change: 57.32
Checkout pipeline showing two product inputs totaling 511.00, a 51.10 discount, 82.78 rounded tax, and 542.68 total with 57.32 change.

4. Collections and methods make the trace maintainable

An array suits a fixed pair of request codes. List<CartLine> suits a cart that can grow. Dictionary<string, Product> gives direct lookup for N101 and P205; TryGetValue safely handles a missing code such as X999, unlike indexing an unknown key.

Method contracts make behaviour testable. AddLine(catalog, code, quantity) returns a valid line or throws a clear validation exception. GetDiscount(511.00m) returns 51.10m, while GetDiscount(499.99m) returns 0m. GetRoundedTax(459.90m) returns 82.78m. Parameters carry input into each method, return values carry results out, and local variables stay inside their scope.

5. Records, classes, interfaces and references

Product and CartLine records are compact data carriers. An interface can isolate behaviour:

csharp
interface IDiscountPolicy { decimal GetDiscount(decimal subtotal); }

sealed class ThresholdDiscount : IDiscountPolicy
{
    readonly decimal threshold;
    readonly decimal rate;
    public ThresholdDiscount(decimal threshold, decimal rate)
    {
        this.threshold = threshold;
        this.rate = rate;
    }
    public decimal GetDiscount(decimal subtotal) =>
        subtotal >= threshold ? subtotal * rate : 0m;
}

IDiscountPolicy policy = new ThresholdDiscount(500m, 0.10m); followed by policy.GetDiscount(511m) returns 51.10m. Encapsulation keeps the rule inside the class, while the interface states the contract and enables runtime dispatch. The checkout depends on that contract, not the concrete rule. The Object Oriented Technology explainer owns the four-pillar Java treatment; this section shows policy dispatch in C#.

Value copying and reference aliasing differ. If int stock = 5; int snapshot = stock; stock = 3;, then snapshot == 5. If two variables refer to one mutable StockBox, setting alias.Value = 3 also makes original.Value == 3. Avoid the misleading claims that every value type is stack-only or every reference type is heap-only.

Object-and-call map showing product dictionary entries N101 and P205, cart quantities 3 and 2, and a 511.00 discount calculation returning 51.10.

6. Validate input, handle failures and keep null explicit

Guard AddLine in this order: reject quantity <= 0 with ArgumentOutOfRangeException; reject X999 with KeyNotFoundException; then reject quantity above stock with InvalidOperationException. Calling AddLine(catalog, "N101", 6) reports Requested quantity 6 exceeds stock 5. It neither returns a line nor reduces stock.

The program catches ArgumentException, KeyNotFoundException and InvalidOperationException at the input boundary, then aborts before totalling a partial cart. Use finally only for cleanup that must run after success or failure.

Nullable references make absence visible: string? couponCode = null; and couponCode?.Trim(). Do not silence a warning with ! unless the program can prove the value is non-null. Comparing Java string handling can sharpen the distinction, but Java rules must not be assumed to be C# rules.

7. C# assessment questions and answer-changing traps

Assessment tasks combine output prediction, type selection, loop traces, overloading versus overriding, interface dispatch, collection choice, and compile-time versus runtime failures. Solve them from C# semantics: trace values before selecting a type, collection or dispatch rule.

Snippet

Tempting answer

Actual result

Repair

7 / 2

3.5

3

Cast before division

i <= cart.Count with two items

Valid indices 0 and 1

Reaches invalid index 2

Use <

x++ + ++x, starting at 5

11

Prints 12; x ends at 7

Trace left to right

Money calculation

Use double

It is not the teaching choice for 511.00m

Use decimal; round at the stated boundary

An interface can declare a discount contract, but it cannot make a poor rule correct. The C++ tutorial and learning path owns reference, pointer, lifetime and virtual-dispatch traces; this section owns C# managed references and interface dispatch.

8. The short version and the next coding step

Use decimal for money, validate before mutating the cart, separate calculations into methods, and hide changeable discounts behind an interface. Checkpoint: subtotal 511.00, discount 51.10, tax 82.78, total 542.68, change 57.32. Next, add coupon SAVE20 for 20.00m off after the threshold discount but before tax, then recalculate and test an invalid coupon.

Continue with Java Programming for adjacent typed OOP or Coding for Placements for broader C, C++, Java and Python practice. Neither page is presented as a C# course.