- Lab
-
Libraries: If you want this lab, consider one of these libraries.
- Core Tech
Trace C# Objects from Construction to Correction
Riverside Diner's payroll clerk has escalated a ticket: hourly employees who work overtime are getting shorted on their paychecks. You are the developer assigned to the small C# console tool that models each worker as an Employee object and computes weekly wages from hourly rate and hours worked. The codebase currently has no working constructor, no way to build a roster of real employees, and no way to turn the timesheet numbers typed at the console into usable constructor and method arguments. Worse, the one wage formula that exists is quietly wrong for anyone who works more than 40 hours. Over the course of the lab you build the Employee type correctly from the ground up, populate and query a roster of employees through their public methods, teach the program to parse console input into the numeric values the objects need, add a lightweight state tracer that lets you watch an employee's fields change across a pay computation the way stepping through code in Visual Studio would, and finally use those traced values to pinpoint and correct the overtime bug so every paycheck comes out right.
Lab Info
Table of Contents
-
Challenge
Step 1: Meet the Riverside Diner payroll bug
The ticket
Riverside Diner's payroll clerk has escalated a bug: hourly employees who work overtime are getting shorted on their paychecks. The diner's small C# console tool models each worker as an
Employeeobject and calculates weekly wages from an hourly rate and hours worked, but the codebase you've inherited is unfinished in more places than the one bug report mentions.You're the developer assigned to fix it -- and to fix it properly, you first have to get the object model itself working. ## The codebase you're inheriting
Open
src/and you'll find six files, each with// TODOmarkers where work is missing:Employee.cs-- theEmployeeclass has fields forName,HourlyRate, andHoursWorked, but no constructor to set them and no methods to report on them.Roster.cs-- meant to build a list of real employees and total their pay, currently empty.InputParser.cs-- meant to turn console-typed text into the numbersEmployeeneeds.StateTracer.cs-- meant to log an employee's field values step by step, the way a debugger would.WageCalculator.cs-- already computes a wage from rate and hours... except it quietly pays every hour at the same rate, overtime included.PayrollReport.cs-- meant to turn a whole roster into a readable, correct report.
Each file compiles as-is, but the
TODOcomments mark exactly what you'll complete. ## The planYou'll work through the codebase in the order a real fix would demand:
- Step 2 -- give
Employeea working constructor and a first method that proves its fields were actually set. - Step 3 -- build a roster of real employees and combine their individual pay summaries into a payroll total.
- Step 4 -- parse raw console-style input into the numbers those objects need, and add a state tracer that shows an employee's fields evolving during a pay computation.
- Step 5 -- use exactly those traced values to pinpoint the overtime bug in
WageCalculator.cs, fix it, and confirm the fix through the full payroll report.
By the end, every paycheck Riverside Diner generates will match what its employees actually earned. ## Running the checks
Each step's work is verified by an automated test file. From the project root you can run the full suite at any point with:
dotnet test tests/Lab.Tests.csproj --nologo --verbosity quietRun it after every task -- a passing test is your signal that a piece of the payroll tool now behaves the way Riverside Diner needs it to. Head into Step 2 to start with the piece everything else depends on: an
Employeethat actually knows its own data.If you get stuck, you can refer to the provided solution code for each task, available in the
solutionfolder.info> This lab experience was developed by the Pluralsight team using an internally developed AI tool. All sections were verified by human experts for accuracy prior to publications. However, content may still contain errors or inaccuracies, and we recommend independent verification.
To report a problem or provide feedback, click here. Feedback may be used to improve accuracy in accordance with our Privacy Policy. -
Challenge
Step 2: Give every Employee a defining constructor
Constructors and field initialization
A class's fields only hold real data if something assigns them. In C#, that something is almost always the constructor: a method with the same name as the class, run automatically by
new, whose job is to take incoming parameters and store them into the object's fields so every instance starts fully formed.Right now,
new Employee("Jamie Ruiz", 14.75, 38)insrc/Employee.cswould compile only once a matching constructor exists -- and until that constructor assigns its parameters toName,HourlyRate, andHoursWorked, every new employee starts with nothing. ## Proving it with an instance methodA constructor that silently sets fields is hard to trust -- you need a way to look at the result. That's what instance methods are for: called on a specific object through dot notation (
employee.Describe()), they can read that object's own fields and hand back something you can inspect.With the constructor in place, the next job is a method that turns an
Employee's three fields into one readable line, so a single call proves the constructor did its job correctly. -
Challenge
Step 3: Populate and query a roster of employees
Calling methods through dot notation
With a constructor and
Describe()working,Employeeis ready to do real payroll work.src/Employee.csalready has aWageCalculator.ComputeWeeklyWage(hourlyRate, hoursWorked)helper available to call -- it computes a wage, even though (as the ticket warns) it isn't yet doing that correctly for every case.Your next job is a method that combines an employee's name with that computed wage, the same way
Describe()combined name, rate, and hours -- another instance method reached throughemployee.GetPaySummary(). ## From one employee to a rosterA single
Employeeobject proves the class works; a diner's payroll needs many of them.src/Roster.csis where you move from constructing one object at a time to building a collection -- aList<Employee>-- that represents everyone on staff.This is also where the overtime bug becomes visible in the data: the roster needs at least one employee whose hours push past 40, since that's exactly the case
WageCalculatorcurrently mishandles. ## Aggregating across the rosterOnce you can build the roster, the payroll clerk's real question is simple: what does the diner owe this week, in total? That means looping over every
Employeein the list and adding each one's wage into a single running number, rather than reporting them one at a time. -
Challenge
Step 4: Turn console input into numbers and trace state
Numeric parsing: double.TryParse
Everything typed at a console -- or read from a timesheet form -- arrives as a
string, butEmployee's constructor needs adoublefor the hourly rate.double.TryParseconverts text to a number without throwing when the text is malformed; it returnstrueorfalseand writes the parsed value into anoutparameter, so you can fall back to a safe default instead of crashing on a clerk's typo.src/InputParser.csis where that conversion happens for hourly rate. ## Numeric parsing: int.ParseHours worked, by contrast, are recorded as whole numbers, and there's no sensible default to fall back to if the text is garbage -- an unreadable hours entry is a data problem worth surfacing loudly.
int.Parsedoes exactly that: it converts text to anintand throws an exception if the text isn't a valid whole number, instead of quietly returning zero.InputParser.csneeds that same conversion for hours worked, deliberately using the stricter of the two parsing styles. ## State tracing: watching fields changeWhen a bug involves a value computed from other values -- exactly the overtime situation here -- the fastest way to find it is to watch the inputs and the output side by side, the way stepping through code in a debugger shows a variable's value at each line.
src/StateTracer.csbuilds a lightweight version of that: a method that reads anEmployee's fields and the wageWageCalculatorproduces from them, and returns each as a labeled log entry in sequence.That log is what you'll use in Step 5 to catch the bug in the act.
-
Challenge
Step 5: Diagnose and correct the wage formula
Diagnosing the arithmetic
Run the tracer from Step 4 against an employee with more than 40 hours and the log tells the story plainly: the wage entry grows by exactly
hourlyRatefor every hour past 40, when it should grow by1.5 * hourlyRate.src/WageCalculator.csis paying overtime hours at the same straight-time rate as regular hours -- the formula never splits the two.Fixing it means separating
hoursWorkedinto a regular portion capped at 40 and an overtime portion for anything beyond, paying each at its own rate. ## Wiring the fix into the reportA correct
WageCalculatoronly matters if the payroll clerk actually sees correct numbers.src/PayrollReport.csis the last piece: a method that turns the whole roster into the multi-line report the clerk reads every week, built from theGetPaySummary()calls you already wrote in Step 3 -- now backed by the fixed formula. ## Confirming the fix reached everyoneOne correct paycheck isn't proof the bug is gone -- the fix needs to hold across the whole roster, including employees who never work overtime at all. A final method in
PayrollReport.cstotals up exactly how much extra pay the overtime fix added across every employee, so a roster with no overtime workers reports zero and a roster like the one from Step 3 reports a real, positive premium. -
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 -- which 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 behaviour 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. The whole suite:
dotnet test tests/Lab.Tests.csproj --nologo --verbosity quietOr 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.