Skip to content

d. Applied Task

Gradual-build lesson (top-level programs)

This version shows only what is new at each step, not the full script every time.

Download Starter Files

Students can download the starter pack here:


Template students should start with (Top-level Program)
using System;

Console.WriteLine("Hello");

No class Program, no Main method.


A) TXT Lesson (Top-level) - only new code each step

TXT Step 1 - Build the menu loop

What this step does

Creates a working menu shell so students can test navigation before adding data structures or file I/O.

New code

using System;

while (true)
{
    Console.Clear();
    Console.WriteLine("=== Student Results (TXT) ===");
    Console.WriteLine("1) Add result");
    Console.WriteLine("2) View results");
    Console.WriteLine("3) Save & Exit");
    Console.Write("Choose: ");

    string choice = Console.ReadLine() ?? "";

    if (choice == "1")
    {
        Console.WriteLine("Add result (not built yet). Press Enter...");
        Console.ReadLine();
    }
    else if (choice == "2")
    {
        Console.WriteLine("View results (not built yet). Press Enter...");
        Console.ReadLine();
    }
    else if (choice == "3")
    {
        Console.WriteLine("Saving (not built yet). Goodbye!");
        break;
    }
    else
    {
        Console.WriteLine("Invalid choice. Press Enter...");
        Console.ReadLine();
    }
}
TXT Step 2 - Add a result type, list, and display method

What this step does

Introduces a StudentResult class and an in-memory list so Option 2 can show stored data.

New code

Add this using at the top:

using System.Collections.Generic;

Add this list before the while (true) loop:

List<StudentResult> results = new List<StudentResult>();

Replace Option 2 inside the loop:

else if (choice == "2")
{
    DisplayResults(results);
    Console.WriteLine("\nPress Enter...");
    Console.ReadLine();
}

Add this helper method after the loop:

static void DisplayResults(List<StudentResult> results)
{
    if (results.Count == 0)
    {
        Console.WriteLine("\n(No results yet)");
        return;
    }

    Console.WriteLine("\nStored results:");
    for (int i = 0; i < results.Count; i++)
        Console.WriteLine($"{i + 1}. {results[i].StudentName} - {results[i].Score}");
}

Add this class at the end of the file:

class StudentResult
{
    public string StudentName { get; set; } = "";
    public int Score { get; set; }
}
TXT Step 3 - Add input validation for new results

What this step does

Replaces the placeholder add action with validated input. Name must not be empty, score must be 0-100.

New code

Replace Option 1 inside the loop:

if (choice == "1")
{
    results.Add(GetNewResultFromUser());
    Console.WriteLine("\nAdded! Press Enter...");
    Console.ReadLine();
}

Add this method after DisplayResults:

static StudentResult GetNewResultFromUser()
{
    string name;
    while (true)
    {
        Console.Write("Enter student name: ");
        name = (Console.ReadLine() ?? "").Trim();
        if (!string.IsNullOrWhiteSpace(name)) break;
        Console.WriteLine("Name cannot be empty.");
    }

    int score;
    while (true)
    {
        Console.Write("Enter score (0-100): ");
        string input = Console.ReadLine() ?? "";
        if (int.TryParse(input, out score) && score >= 0 && score <= 100) break;
        Console.WriteLine("Score must be an integer from 0 to 100.");
    }

    return new StudentResult { StudentName = name, Score = score };
}
TXT Step 4 - Load existing TXT data at startup

What this step does

Loads previously saved results before the menu starts, so data persists between runs.

New code

Add this using at the top:

using System.IO;

Replace list initialization before the loop:

const string FilePath = "results.txt";

List<StudentResult> results = LoadResults();

Add this line to the menu display:

Console.WriteLine($"Loaded records: {results.Count}");

Add this method after GetNewResultFromUser:

static List<StudentResult> LoadResults()
{
    var results = new List<StudentResult>();

    if (!File.Exists(FilePath))
        return results;

    string[] lines = File.ReadAllLines(FilePath);

    foreach (string rawLine in lines)
    {
        string line = rawLine.Trim();
        if (string.IsNullOrWhiteSpace(line)) continue;

        if (line.Equals("StudentName|Score", StringComparison.OrdinalIgnoreCase))
            continue;

        string[] parts = line.Split('|');
        if (parts.Length != 2) continue;

        string name = parts[0].Trim();
        if (int.TryParse(parts[1].Trim(), out int score))
            results.Add(new StudentResult { StudentName = name, Score = score });
    }

    return results;
}
TXT Step 5 - Save updated TXT data when exiting

What this step does

Writes all in-memory results back to file so new records are kept after the program closes.

New code

Replace Option 3 inside the loop:

else if (choice == "3")
{
    SaveResults(results);
    Console.WriteLine("Saved. Goodbye!");
    break;
}

Add this method after LoadResults:

static void SaveResults(List<StudentResult> results)
{
    using StreamWriter writer = new StreamWriter(FilePath, false);
    writer.WriteLine("StudentName|Score");

    foreach (var r in results)
    {
        string safeName = r.StudentName.Replace("|", " ");
        writer.WriteLine($"{safeName}|{r.Score}");
    }
}

TXT complete (compile-safe file order)

Use this order in the file:

  1. using lines
  2. FilePath constant and results = LoadResults()
  3. main while loop
  4. helper methods (LoadResults, SaveResults, GetNewResultFromUser, DisplayResults)
  5. StudentResult class at the end

B) CSV Lesson (Top-level) - only what is new

Build TXT first, then apply only these changes for CSV.

CSV Step 1 - Update menu heading

What this step does

Makes the UI clearly indicate the format being used.

New code

Change the heading line:

Console.WriteLine("=== Student Results (CSV) ===");
CSV Step 2 - No model change

What this step does

No change. StudentResult and List<StudentResult> stay the same.

CSV Step 3 - No validation change

What this step does

No change. GetNewResultFromUser() is reused exactly as TXT.

CSV Step 4 - Change load logic for comma-delimited data

What this step does

Switches loading rules from pipe-separated TXT to comma-separated CSV.

New code

Change file path constant:

const string FilePath = "results.csv";

Replace LoadResults() with:

static List<StudentResult> LoadResults()
{
    var results = new List<StudentResult>();

    if (!File.Exists(FilePath))
        return results;

    string[] lines = File.ReadAllLines(FilePath);

    foreach (string rawLine in lines)
    {
        string line = rawLine.Trim();
        if (string.IsNullOrWhiteSpace(line)) continue;

        if (line.Equals("StudentName,Score", StringComparison.OrdinalIgnoreCase))
            continue;

        string[] parts = line.Split(',');
        if (parts.Length != 2) continue;

        string name = parts[0].Trim();
        if (int.TryParse(parts[1].Trim(), out int score))
            results.Add(new StudentResult { StudentName = name, Score = score });
    }

    return results;
}
CSV Step 5 - Change save logic for comma-delimited data

What this step does

Saves records using CSV headers and comma separation.

New code

Replace SaveResults() with:

static void SaveResults(List<StudentResult> results)
{
    using StreamWriter writer = new StreamWriter(FilePath, false);
    writer.WriteLine("StudentName,Score");

    foreach (var r in results)
    {
        string safeName = r.StudentName.Replace(",", " ");
        writer.WriteLine($"{safeName},{r.Score}");
    }
}

C) XML Lesson (Top-level) - only what is new

Build TXT first, then apply only these changes for XML.

XML Step 1 - Update menu heading

What this step does

Makes the UI clearly indicate XML mode.

New code

Change the heading line:

Console.WriteLine("=== Student Results (XML) ===");
XML Step 2 - No model change

What this step does

No change. StudentResult and List<StudentResult> stay the same.

XML Step 3 - No validation change

What this step does

No change. GetNewResultFromUser() is reused exactly as TXT.

XML Step 4 - Change load logic to parse XML elements

What this step does

Loads from XML nodes (<Result>, <StudentName>, <Score>) instead of split text lines.

New code

Add this using:

using System.Xml.Linq;

Change file path constant:

const string FilePath = "results.xml";

Replace LoadResults() with:

static List<StudentResult> LoadResults()
{
    var results = new List<StudentResult>();

    if (!File.Exists(FilePath))
        return results;

    XDocument doc = XDocument.Load(FilePath);
    XElement root = doc.Root;
    if (root == null)
        return results;

    foreach (XElement element in root.Elements("Result"))
    {
        string name = (string)element.Element("StudentName");
        string scoreText = (string)element.Element("Score");

        if (string.IsNullOrWhiteSpace(name)) continue;
        if (int.TryParse(scoreText, out int score))
            results.Add(new StudentResult { StudentName = name.Trim(), Score = score });
    }

    return results;
}
XML Step 5 - Change save logic to write XML structure

What this step does

Creates a structured XML document and writes it to disk.

New code

Replace SaveResults() with:

static void SaveResults(List<StudentResult> results)
{
    XElement root = new XElement("Results");

    foreach (var r in results)
    {
        root.Add(
            new XElement("Result",
                new XElement("StudentName", r.StudentName),
                new XElement("Score", r.Score)
            )
        );
    }

    XDocument doc = new XDocument(root);
    doc.Save(FilePath);
}

40+ response tip

For format-justification questions, always compare:

  • structure (TXT separator vs CSV delimiter vs XML tags/tree)
  • reason for use in the scenario
  • one trade-off for the two formats not chosen