- Lab
-
Libraries: If you want this lab, consider one of these libraries.
- Core Tech
Shape a Console Application with C# Syntax
Cascade Harbor Depot needs a console tool its dock staff can run each shift. Build the tool to compute storage fees, decide handling priority, process arriving crates without looping past dock capacity, parse hand-typed crate codes, track warehouse bay placement, and keep application state in validated classes instead of loose variables scattered through `Program.cs`.
Lab Info
Table of Contents
-
Challenge
Step 1: Tour the Cascade Harbor Depot console app
Cascade Harbor Depot
Cascade Harbor Depot's dock staff need a console tool for each shift to log arriving crates, calculate what each one costs to store, decide how urgently each crate should be handled, and track which warehouse bay it ends up in. You'll build that tool in C#, practicing the language's specific rules for typing, operators, methods, and classes, including details that may differ from similar syntax in other languages.
The starter project already has a
src/folder with aProgram.csentry point and several files with methods that return placeholder values. You'll complete those methods across the next four coding steps. ## The files you'll shapesrc/FeeCalculator.csturns a crate's weight and days stored into a fee with tiered discounts.src/IntakeQueue.csclassifies crate priority and processes the arrival queue against dock capacity.src/CrateBatch.csandsrc/CrateCodeParser.csrefactor the fee logic into reusable, overloaded, and flexible-argument methods, and parse hand-typed input safely.src/CrateTracker.cs,src/WarehouseGrid.cs,src/Crate.cs, andsrc/Warehouse.csmove from loose arrays into validatedCrateandWarehouseclasses, each living in its own file.
Program.csties all of it together as the console entry point dock staff run. You won't touch every file in every step. Each step's tasks are grouped by the files where that concept applies. ## Where you're headedBy the end of Step 5, running the console app will log a shift's worth of crates, compute each crate's discounted fee, sort crates into priority lanes, stop accepting crates once the dock queue tops out, and report which warehouse bays are occupied, all backed by classes that refuse to hold invalid data rather than trusting whatever
Program.cshappens to hand them.Start with the piece every other feature depends on: figuring out what a crate actually costs to store.
-
Challenge
Step 2: Calculate crate fees with typed statements
Typed variables and constants
C# is statically typed: every variable's type is fixed at compile time, and the compiler enforces it long before the console app runs. A rate that never changes, like a per-kilogram storage charge, belongs in a
const decimalfield, not a bare literal buried in an expression. Naming it makes the fee formula insrc/FeeCalculator.csreadable and keeps the rate in exactly one place if the depot ever changes its pricing.decimalis the type you want for money and weights here, notdouble, because it avoids the binary rounding surprises that creep into floating-point currency math. ## Operator precedence and groupingC# follows the arithmetic precedence you'd expect: multiplication before addition. But discount math is exactly where that default order goes wrong if you're not deliberate. A discount percentage has to be multiplied against the whole base fee before it's subtracted, and parentheses are how you make that explicit instead of hoping the compiler reads your intent. A small expression like
baseFee - percentage * baseFeecan be fine whenpercentageis already a decimal rate. Once percentages or extra terms enter the formula, grouping keeps the compiler's math aligned with your intent.With the base fee working, apply the tiered discount on top of it.
-
Challenge
Step 3: Route the crate intake queue with decisions and loops
if/elsedecisionsEvery crate that arrives needs a handling priority, and that decision depends on more than one fact about the crate: whether it's fragile, and how much it weighs.
if/else if/elsechains let you check conditions in a deliberate order, which matters here. Checking fragility first means a heavy, fragile crate is still classified as urgent rather than merely standard. Order changes the outcome, not just the readability. ##switchexpressions Once a crate has a priority string, turning that string into a dock lane number is a straight lookup, exactly what aswitchexpression is for. C#'sswitchexpression returns a value directly from the matched arm, and a discard pattern (_) arm catches anything that doesn't match one of your known cases. That matters when the priority string came from somewhere you don't fully trust. ## Loop termination withbreakandcontinueA dock has a hard capacity, and the truck doesn't stop unloading just because a scanner misread a weight.continueskips the rest of the current loop iteration, which is perfect for ignoring a bad reading without abandoning the whole queue.breakexits the loop entirely, which is what you want the instant the dock hits capacity. Using the wrong one here either stops the shift early on one bad scan or keeps counting crates past what the dock can physically hold. -
Challenge
Step 4: Refactor fee logic into methods and parse crate codes safely
Method extraction and overloading
Once
src/CrateBatch.cshas one method that sums a batch's weight, a second scenario shows up: the same summing logic, but padded by a safety buffer percentage. Rather than writing a second, separately named method that duplicates the summing, C# lets you overloadCalculateBatchWeight: declare it again with a different parameter list, and the compiler picks the right one from the call site's arguments. The two-argument overload can call the one-argument overload directly, so the summing logic only exists once. ##paramsfor flexible argumentsSometimes you don't know in advance how many fee amounts you're combining: one crate, or a truckload. Use a
params decimal[]parameter when you want to pass any number ofdecimalarguments directly, without building an array first. C# packs those arguments into an array. Inside the method, you work with that array like any other; the flexibility is at the call site. ## Safe text parsing withTryParseA weight typed by hand at a console prompt is just a string, and strings typed by hand are typed wrong sometimes.
decimal.Parsethrows on bad input, which would crash the shift's console session over one mistyped digit.decimal.TryParseinstead returns abooltelling you whether parsing succeeded, with the parsed value delivered through anoutparameter. That lets you handle bad input as an ordinary return value instead of an exception. -
Challenge
Step 5: Track crates and bays with validated classes
One-dimensional array traversal
A shift's arriving crate codes live in a plain
string[], and finding one means walking the array by index and comparing entries until you find a match or reach the end without one. Returning-1for "not found" is a long-standing convention in C# arrays: it's a valid index nowhere in the array, so callers can check for it without a second flag. ## Multidimensional array traversalThe warehouse bay layout is naturally two-dimensional: rows and columns of physical bays. C#'s
bool[,]models that directly as a single rectangular array rather than an array of arrays. Traverse it with nested loops bounded byGetLength(0)andGetLength(1), rather than a singleLength, since the two dimensions can differ. ## Constructors and property validationSo far, weights and codes have been raw values passed between methods, trusted at every step. A
Crateclass changes that: its constructor is the one place invalid data gets rejected by throwing before an invalid object can ever exist. Once construction succeeds, every other method that receives aCratecan trust itsCodeandWeightKgwithout re-checking them. ## Splitting classes across filesCrateandWarehouselive in their own files,src/Crate.csandsrc/Warehouse.cs, because each class has one job and one reason to change.Warehousedepends onCratebecause it stores crates in bays, but neither file needs to know aboutProgram.cs.Program.cssimply constructs both classes and calls their methods. That separation letsAssignCrateToBayfocus on bounds and occupancy, leaving crate validation to theCrateconstructor you just wrote. -
Challenge
Step 6: Run the application
Every task is done, and each one was graded the moment you completed it. What you have not done yet is watch the whole application run at once. This is the only place the pieces you wrote separately become a single working program.
Start the application
Open the Terminal tab and run:
dotnet run --project src/Lab.csprojThe program runs in the terminal and prints its output there. Every behavior it shows is code you wrote in the previous steps, so read the output against what each task asked for. That is the whole lab, working as one program. ### Run the checks yourself
The lab ran each task's test for you as you went, but they are ordinary tests and you can run them whenever you like.
To run the whole suite:
dotnet test tests/Lab.Tests.csproj --nologo --verbosity quietTo run one task at a time:
bash runTest.sh Task_2_1Each failure names the task it belongs to, so the output tells you which step to go back to rather than which line to stare at. Two things worth trying before you finish: break something on purpose and watch the matching check fail, then put it back. Knowing what a failure looks like is worth as much as knowing what a pass looks like, and it costs you nothing here.
When you are done, everything you wrote is still in the editor tabs. The lab is yours to keep reading.
About the author
Real skill practice before real-world application
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Learn by doing
Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.
Follow your guide
All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.
Turn time into mastery
On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.