Task 1 — Invoice Line Calculator
Topics: variables, data types, operators, type casting, if
Goal: Calculate line total with discount & GST safely.
Input: qty (int), rate (decimal), discountPercent (int/string)
Rules:
- If discountPercent comes as string, safely cast/parse
- If qty <= 0 or rate <= 0 → reject
Done when: returns (netAmount, taxAmount, totalAmount) as decimal
Task 2 — Login Attempt Validator
Topics: conditional statements (if), operators
Goal: Validate a login attempt.
Rules:
- If password length < 8 → “weak password”
- If username is null/empty → error
- If OTP provided, it must be 6 digits (type casting + checks)
Done when: returns a structured result (success + message)
Task 3 — Data Cleanup Loop
Topics: loops, break, continue
Goal: Process a list of strings and clean them.
Rules:
- Skip empty lines (continue)
- Stop processing if you encounter
"STOP"(break) - Count valid lines, invalid lines
Done when: prints summary counts
Task 4 — “Utility Methods” Mini Library
Topics: C# methods
Create static methods:
bool IsValidEmail(string input)int Clamp(int value, int min, int max)decimal RoundMoney(decimal amount)
Done when: called from Main() with sample inputs
Task 5 — OOP: Order + Customer Model
Topics: OOPS and classes
Create classes:
Customer { Id, Name }Order { Id, CustomerId, Amount, Status }
Add behavior:
Order.MarkPaid()Order.Cancel(reason)
Done when: state changes are protected by rules (if checks)
Task 6 — Generics: Result Wrapper
Topics: generics, modern language features (nullable)
Create:
Result<T> { bool Success; T? Value; string? Error; }
And helper:
Result<T>.Ok(value)Result<T>.Fail(error)
Done when: you return Result<Order> from a method
Task 7 — Collections: In-Memory Cache
Topics: collections, system namespaces
Build a cache service:
- Use
Dictionary<string, object>orDictionary<string, T>pattern - Methods:
Set(key, value),TryGet(key, out value),Remove(key)
Done when: cache is used by Task 5 service layer
Task 8 — I/O: CSV Reader + Writer
Topics: I/O, collections, loops
Read orders.csv and load into a List<Order>.
Rules:
- Skip header row
- Handle bad rows with
continue(don’t crash) - Write
orders_clean.csv
Done when: clean file is generated and counts printed
Task 9 — Pattern Matching: Event Classifier
Topics: pattern matching, modern language features (switch expression)
Given an input object (Order, Exception, or string), classify it:
- If
Order { Amount: >= 100000 }→ “HighValueOrder” - If exception is
TimeoutException→ “Timeout” - If string contains “failed” → “FailureText”
Done when: switch + property/relational patterns are used
Task 10 — Async Programming: File Processing
Topics: async programming, I/O
Read a large text file using async (ReadLineAsync) and count:
- total lines
- error lines (contains “ERROR”)
Done when: uses async Task properly (no .Result / blocking)
Task 11 — Multithreading: Concurrent Aggregation
Topics: multithreading, collections
Simulate 5 threads adding metrics into shared state.
Rules:
- Use
ConcurrentDictionary<string,int>orlockcorrectly - Show correct final totals
Done when: results are consistent across multiple runs
Task 12 — CLR + Memory Management Lab
Topics: CLR concepts, memory management
Write a small benchmark that compares
A) Using string.Split('|') in a loop
B) Using ReadOnlySpan<char> slicing (no split)
Print:
StopwatchtimeGC.CollectionCount(0/1/2)GC.GetTotalMemory(false)
Done when: you can observe allocation/GC difference
Optional “Advanced Bonus” Task — Source Generator (Only if you want)
Topics: source generators
Make an attribute like [AutoToString] and generate a ToString() implementation for marked classes.
Done when: build produces generated code and it appears in IntelliSense/build output.
7-Day Practical Plan (2 tasks/day, last day = merge + bonus)
Day 1 — Fundamentals
Task 1: Invoice Line Calculator
Task 2: Login Attempt Validator
Day 2 — Loops + Methods
Task 3: Data Cleanup Loop (break/continue)
Task 4: Utility Methods Mini Library
Day 3 — OOP Core
Task 5: Customer + Order Classes (rules inside methods)
Task 6: Generic
Result<T>wrapper
Day 4 — Collections + I/O
Task 7: In-Memory Cache (Dictionary-based)
Task 8: CSV Reader + Writer for Orders
Day 5 — Modern Features
Task 9: Pattern Matching Classifier
Task 10: Async File Processing (ReadLineAsync)
Day 6 — Concurrency
Task 11: Multithreading + Concurrent aggregation
Task 12: CLR + Memory benchmark (Split vs Span)
Day 7 — Combine
Combine Tasks 5–10 into a mini “Order Processor CLI”
(Optional) Source Generator bonus: AutoToString
Starter Solution Structure (simple + realistic)
Create a folder CSharpPractice and create a solution with 3 projects:
Practice.Core(classes, services, utilities)Practice.Cli(console runner: Main, menu/commands)Practice.Tests(optional later)
You can start with just Practice.Core + Practice.Cli.
Copy-paste Skeleton Code
0) Practice.Cli/Program.cs (menu runner)
Console.WriteLine(“=== C# Practice Runner ===”);
Console.WriteLine(“1. Task1 Invoice”);
Console.WriteLine(“2. Task2 Login Validator”);
Console.WriteLine(“3. Task3 Cleanup Loop”);
Console.WriteLine(“4. Task4 Utilities”);
Console.WriteLine(“5. Task5 OOP Order”);
Console.WriteLine(“6. Task6 Generic Result”);
Console.WriteLine(“7. Task7 Cache”);
Console.WriteLine(“8. Task8 CSV IO”);
Console.WriteLine(“9. Task9 Pattern Matching”);
Console.WriteLine(“10. Task10 Async File”);
Console.WriteLine(“11. Task11 Multithreading”);
Console.WriteLine(“12. Task12 CLR + Memory Bench”);
Console.Write(“Choose: “);
var choice = Console.ReadLine();
switch (choice)
{
case “1”: Task1Invoice.Run(); break;
case “2”: Task2Login.Run(); break;
case “3”: Task3Cleanup.Run(); break;
case “4”: Task4Utilities.Run(); break;
case “5”: Task5OopOrder.Run(); break;
case “6”: Task6Result.Run(); break;
case “7”: Task7Cache.Run(); break;
case “8”: Task8Csv.Run(); break;
case “9”: Task9Pattern.Run(); break;
case “10”: await Task10AsyncFile.RunAsync(); break;
case “11”: await Task11Threading.RunAsync(); break;
case “12”: Task12ClrMemoryBench.Run(); break;
default: Console.WriteLine(“Invalid choice.”); break;
}
Create folder: Practice.Core/Tasks/ and add the following classes.
Task 1 — Invoice Line Calculator
Topics: variables, data types, casting/parsing, operators, if, methods
Practice.Core/Tasks/Task1Invoice.cs
public static class Task1Invoice
{
public static void Run()
{
Console.WriteLine(“Task1: Invoice Calculator”);
int qty = 3;
decimal rate = 199.50m;
string discountPercentText = “10”; // could be “abc”
decimal gstPercent = 18m;
var result = CalculateLine(qty, rate, discountPercentText, gstPercent);
Console.WriteLine($“Success: {result.Success}”);
Console.WriteLine(result.Success
? $“Net={result.Value.NetAmount}, Tax={result.Value.TaxAmount}, Total={result.Value.TotalAmount}”
: $“Error: {result.Error}”);
}
public static Result<InvoiceLineTotals> CalculateLine(int qty, decimal rate, string discountPercentText, decimal gstPercent)
{
// TODO: validate qty > 0 and rate > 0
// TODO: parse discountPercentText safely (TryParse)
// TODO: compute:
// gross = qty * rate
// discount = gross * discountPercent/100
// net = gross – discount
// tax = net * gstPercent/100
// total = net + tax
// TODO: return Result.Ok(totals) or Result.Fail(error)
return Result<InvoiceLineTotals>.Fail(“TODO: implement Task1”);
}
}
public readonly record struct InvoiceLineTotals(decimal NetAmount, decimal TaxAmount, decimal TotalAmount);
Task 2 — Login Attempt Validator
Topics: if, operators, parsing/casting, methods
Practice.Core/Tasks/Task2Login.cs
public static class Task2Login
{
public static void Run()
{
Console.WriteLine(“Task2: Login Validator”);
string? username = “kishore”;
string password = “Pass@123”;
string? otp = “123456”; // can be null, or invalid
var result = Validate(username, password, otp);
Console.WriteLine(result.Success ? “Login input valid” : $“Invalid: {result.Error}”);
}
public static Result<bool> Validate(string? username, string password, string? otp)
{
// TODO:
// if username null/empty => fail
// if password length < 8 => fail
// if otp provided => must be exactly 6 digits and parseable int
// return Ok(true) when valid
return Result<bool>.Fail(“TODO: implement Task2”);
}
}
Task 3 — Data Cleanup Loop
Topics: loops, break, continue
Practice.Core/Tasks/Task3Cleanup.cs
public static class Task3Cleanup
{
public static void Run()
{
Console.WriteLine(“Task3: Cleanup Loop (break/continue)”);
var lines = new[]
{
“”, ” “, “#comment”, “OK:1”, “BAD”, “OK:2”, “STOP”, “OK:3”
};
int valid = 0, invalid = 0, skipped = 0;
foreach (var raw in lines)
{
// TODO:
// – trim
// – if empty => skipped++ and continue
// – if starts with “#” => skipped++ and continue
// – if equals “STOP” => break
// – else if starts with “OK:” => valid++
// – else invalid++
}
Console.WriteLine($“Valid={valid}, Invalid={invalid}, Skipped={skipped}”);
}
}
Task 4 — Utility Methods Mini Library
Topics: methods, basic namespaces
Practice.Core/Tasks/Task4Utilities.cs
namespace Practice.Core.Tasks;
public static class Task4Utilities
{
public static void Run()
{
Console.WriteLine(“Task4: Utilities”);
Console.WriteLine(IsValidEmail(“test@example.com”));
Console.WriteLine(Clamp(15, 0, 10));
Console.WriteLine(RoundMoney(123.4567m));
}
public static bool IsValidEmail(string input)
{
// TODO: simple production-safe check (not perfect RFC)
// Hint: use Regex or basic contains ‘@’ and ‘.’
return false;
}
public static int Clamp(int value, int min, int max)
{
// TODO
return 0;
}
public static decimal RoundMoney(decimal amount)
{
// TODO: round to 2 decimals (MidpointRounding.AwayFromZero)
return 0m;
}
}
Task 5 — OOP: Order + Customer
Topics: classes, encapsulation, if rules, methods
Practice.Core/Tasks/Task5OopOrder.cs
public static class Task5OopOrder
{
public static void Run()
{
Console.WriteLine(“Task5: OOP Order”);
var customer = new Customer(Guid.NewGuid(), “Kishore”);
var order = new Order(Guid.NewGuid(), customer.Id, 1200m);
Console.WriteLine(order);
var paid = order.MarkPaid();
Console.WriteLine(paid.Success ? “Paid” : paid.Error);
var cancel = order.Cancel(“Customer requested”);
Console.WriteLine(cancel.Success ? “Cancelled” : cancel.Error);
Console.WriteLine(order);
}
}
public sealed class Customer
{
public Guid Id { get; }
public string Name { get; }
public Customer(Guid id, string name)
{
// TODO: validate
Id = id;
Name = name;
}
}
public enum OrderStatus { Draft, Paid, Cancelled }
public sealed class Order
{
public Guid Id { get; }
public Guid CustomerId { get; }
public decimal Amount { get; }
public OrderStatus Status { get; private set; } = OrderStatus.Draft;
public string? CancelReason { get; private set; }
public Order(Guid id, Guid customerId, decimal amount)
{
// TODO: validate (amount > 0)
Id = id;
CustomerId = customerId;
Amount = amount;
}
public Result<bool> MarkPaid()
{
// TODO:
// if already Cancelled => fail
// if already Paid => fail
// else set Paid
return Result<bool>.Fail(“TODO: implement MarkPaid”);
}
public Result<bool> Cancel(string reason)
{
// TODO:
// if Paid => fail
// if reason empty => fail
// else set Cancelled + CancelReason
return Result<bool>.Fail(“TODO: implement Cancel”);
}
public override string ToString()
=> $“Order(Id={Id}, Amount={Amount}, Status={Status}, CancelReason={CancelReason})”;
}
Task 6 — Generics: Result<T>
Topics: generics, nullable, modern features
Practice.Core/Tasks/Task6Result.cs
public static class Task6Result
{
public static void Run()
{
Console.WriteLine(“Task6: Result<T>”);
var ok = Result<int>.Ok(10);
var fail = Result<int>.Fail(“No value”);
Console.WriteLine(ok.Success ? ok.Value : ok.Error);
Console.WriteLine(fail.Success ? fail.Value : fail.Error);
}
}
public readonly record struct Result<T>(bool Success, T? Value, string? Error)
{
public static Result<T> Ok(T value) => new(true, value, null);
public static Result<T> Fail(string error) => new(false, default, error);
}
Task 7 — Collections: In-Memory Cache
Topics: Dictionary, generics
Practice.Core/Tasks/Task7Cache.cs
public static class Task7Cache
{
public static void Run()
{
Console.WriteLine(“Task7: Cache”);
var cache = new SimpleCache();
cache.Set(“x”, 10);
cache.Set(“user”, “kishore”);
Console.WriteLine(cache.TryGet<int>(“x”, out var xVal) ? xVal : “missing”);
Console.WriteLine(cache.TryGet<string>(“user”, out var name) ? name : “missing”);
}
}
public sealed class SimpleCache
{
private readonly Dictionary<string, object> _data = new();
public void Set<T>(string key, T value) where T : notnull
{
// TODO: validate key
_data[key] = value;
}
public bool TryGet<T>(string key, out T? value)
{
// TODO: safe type check + cast
value = default;
return false;
}
public bool Remove(string key)
{
// TODO
return false;
}
}
Task 8 — I/O: Orders CSV Reader + Writer
Topics: System.IO, collections, loops, continue
Practice.Core/Tasks/Task8Csv.cs
namespace Practice.Core.Tasks;
public static class Task8Csv
{
public static void Run()
{
Console.WriteLine(“Task8: CSV IO”);
var input = “orders.csv”;
var output = “orders_clean.csv”;
// TODO: create a sample orders.csv if it doesn’t exist (File.WriteAllText)
// Format:
// Id,CustomerId,Amount,Status
// <guid>,<guid>,1200.50,Draft
var orders = ReadOrders(input);
WriteOrders(output, orders);
Console.WriteLine($“Read={orders.Count}, Wrote={output}”);
}
public static List<OrderRow> ReadOrders(string path)
{
var list = new List<OrderRow>();
// TODO:
// – read all lines or stream lines
// – skip header
// – for each row parse guid, decimal, status
// – on bad row => continue (don’t crash)
return list;
}
public static void WriteOrders(string path, List<OrderRow> rows)
{
// TODO: write header + rows
}
}
public readonly record struct OrderRow(Guid Id, Guid CustomerId, decimal Amount, string Status);
Task 9 — Pattern Matching: Classifier
Topics: switch expression, type patterns, property/relational patterns
Practice.Core/Tasks/Task9Pattern.cs
public static class Task9Pattern
{
public static void Run()
{
Console.WriteLine(“Task9: Pattern Matching”);
object a = new Order(Guid.NewGuid(), Guid.NewGuid(), 150000m);
object b = new TimeoutException(“db timeout”);
object c = “payment failed due to timeout”;
Console.WriteLine(Classify(a));
Console.WriteLine(Classify(b));
Console.WriteLine(Classify(c));
}
public static string Classify(object input)
{
// TODO: Use a switch expression with:
// – Order { Amount: >= 100000 } => “HighValueOrder”
// – TimeoutException => “Timeout”
// – string s when s.Contains(“failed”, StringComparison.OrdinalIgnoreCase) => “FailureText”
// – _ => “Other”
return “TODO”;
}
}
Task 10 — Async File Processing
Topics: async/await, I/O, loops
Practice.Core/Tasks/Task10AsyncFile.cs
public static class Task10AsyncFile
{
public static async Task RunAsync()
{
Console.WriteLine(“Task10: Async File Processing”);
var path = “telemetry_sample.log”;
// TODO: create file if not exists with some lines containing “ERROR”
var (total, errors) = await CountAsync(path);
Console.WriteLine($“Total={total}, Errors={errors}”);
}
public static async Task<(int total, int errors)> CountAsync(string path)
{
int total = 0, errors = 0;
// TODO:
// using var reader = new StreamReader(path);
// while ((line = await reader.ReadLineAsync()) != null)
// {
// total++;
// if (line.Contains(“ERROR”)) errors++;
// }
return (total, errors);
}
}
Task 11 — Multithreading: Concurrent Aggregation
Topics: multithreading, ConcurrentDictionary OR lock
Practice.Core/Tasks/Task11Threading.cs
namespace Practice.Core.Tasks;
public static class Task11Threading
{
public static async Task RunAsync()
{
Console.WriteLine(“Task11: Multithreading Aggregation”);
var counts = new ConcurrentDictionary<string, int>();
var services = new[] { “Auth”, “Payments”, “Orders” };
int iterationsPerTask = 100_000;
var tasks = new List<Task>();
for (int t = 0; t < 5; t++)
{
tasks.Add(Task.Run(() =>
{
// TODO:
// loop iterationsPerTask times
// pick service using modulo
// increment counts safely (AddOrUpdate)
}));
}
await Task.WhenAll(tasks);
foreach (var kv in counts)
Console.WriteLine($“{kv.Key} => {kv.Value}”);
}
}
Task 12 — CLR + Memory Benchmark: Split vs Span
Topics: CLR, GC, memory, modern features
Practice.Core/Tasks/Task12ClrMemoryBench.cs
namespace Practice.Core.Tasks;
public static class Task12ClrMemoryBench
{
public static void Run()
{
Console.WriteLine(“Task12: CLR + Memory Bench”);
var lines = GenerateSampleLines(300_000);
Bench(“Split”, () => ParseWithSplit(lines));
Bench(“Span”, () => ParseWithSpan(lines));
}
private static void Bench(string name, Action action)
{
var before0 = GC.CollectionCount(0);
var before1 = GC.CollectionCount(1);
var before2 = GC.CollectionCount(2);
var memBefore = GC.GetTotalMemory(false);
var sw = Stopwatch.StartNew();
action();
sw.Stop();
var memAfter = GC.GetTotalMemory(false);
var after0 = GC.CollectionCount(0);
var after1 = GC.CollectionCount(1);
var after2 = GC.CollectionCount(2);
Console.WriteLine($“{name}: time={sw.ElapsedMilliseconds}ms, memΔ={(memAfter – memBefore)}, GC0Δ={after0-before0}, GC1Δ={after1-before1}, GC2Δ={after2-before2}”);
}
private static string[] GenerateSampleLines(int n)
{
var arr = new string[n];
for (int i = 0; i < n; i++)
arr[i] = $“2026-02-20T04:11:{i%60:00}Z|INFO|Auth|1001|{i%1500}|u-{i%200}|Login ok”;
return arr;
}
private static void ParseWithSplit(string[] lines)
{
int sum = 0;
foreach (var line in lines)
{
// TODO: parse duration using Split(‘|’) and int.TryParse
// sum += duration
}
if (sum == –1) Console.WriteLine(“impossible”); // avoid dead-code elimination
}
private static void ParseWithSpan(string[] lines)
{
int sum = 0;
foreach (var line in lines)
{
// TODO:
// ReadOnlySpan<char> s = line.AsSpan();
// Extract the 5th field (duration) without Split
// Parse int.TryParse(durationSpan, out var duration)
// sum += duration
}
if (sum == –1) Console.WriteLine(“impossible”);
}
}
Day 7: Combine Tasks (Mini “Order Processor”)
When Tasks 5–10 are done, combine into a small flow:
Read orders from CSV (Task 8)
Validate + apply business rules using methods (Tasks 4–6)
Cache customer lookups (Task 7)
Classify orders/events using pattern matching (Task 9)
Use async file reading for logs/notifications (Task 10)
Recent Comments