Files
learning-hour-maker/exercises/slice-with-lambda/README.md
T
Your Name ac45255a80 Slice with lambda: add C# exercise projects and export
- Add SliceWithLambda.slnx + csproj scaffolding for ideas/slice-with-lambda/
  exercise/csharp (examples project + xUnit solution/test project)
- Add exercises/slice-with-lambda (C# version of the prefactored-longrange
  exercise with Buyers and SliceWithLambda projects)
- Add export/slice-with-lambda (nested git repo with exercise export)
- Ignore bin/ and obj/ build output at repo root
2026-09-30 00:01:12 +00:00

3.9 KiB

Slice with Lambda — C# exercises

Exercises for the Slice with Lambda learning hour (C# variant).

Open SliceWithLambda.sln in Rider (or run dotnet test — see below). Requires the .NET 10 SDK (pinned by global.json).

What's in the box

folder what start state
src/SliceWithLambda/ three demo examples (pristine) the refactoring not done yet
src/Buyers/ the pair exercise (pristine) one loop, one lambda-able expression
solution/ reference end states (facilitator + fast pairs) mechanics done, tests stub through the seam
tests/ test projects see the red/green table

The three demo examples (src/SliceWithLambda/)

file guard (client state) unwanted dependency lambda tail
MetaphorFree.cs if (a < 0) return 0; _someDependency.mightThrowWhenPreconditionNotSatisfied(a) Func<int, int> readB = a => ... int c = a + b; return c * 2;
WeatherStation.cs if (_wateredToday) _lightSensor.Read() (byte) Func<byte> readLight = () => ... sunny -> WateringDuration.Medium, shady -> Short (reading > 128 => SunExposure.Sunny)
IceCreamShop.cs if (_thawedToday) _thermometer.Read() (byte) Func<byte> readTemperature = () => ... warm -> ThawAmount.Medium, cold -> Low (temperature > 25 => Warmth.Warm)

In all three, the guard reads client state (a parameter or a flag the client owns), so it stays in the extracted method. Only the dependency call moves into the lambda.

MetaphorFree.cs is the smallest possible shape: no loop, one dependency call, a tail of one arithmetic step. It carries the capture-to-parameter lesson: the lambda takes a as its parameter because a lambda moved up into the function head cannot capture method parameters. WeatherStation.cs and IceCreamShop.cs are same-shape domain repetitions, so participants can practice extracting the lambda twice — the C# delegate syntax (Func<byte>) is worth the repetition.

The pair exercise (src/Buyers/)

Buyers.ShoppingListTotal(buyerName) computes a buyer's shopping-list total: one loop, and exactly one lambda-able expression — priceList.PriceFor(flavour). PriceList adds random surge pricing, so the test in tests/BuyersTests/ is red or flaky by design: it stands for the external pricing service.

Do the mechanics on the whole method:

  1. Extract the dependency-using code into a lambda and slide it to the top (Func<IceCream, int> priceFor = flavour => priceList.PriceFor(flavour); — the invocation in the loop stays put).
  2. Extract the loop into a method taking the lambda as a parameter.
  3. Keep a no-arg overload for callers (pass the method group priceList.PriceFor as the "real" pricing function) and make the test pass with a pure lambda (_ => 2): 2 flavours x 2 scoops x 2 = 8.

Reference end state: solution/Buyers/ (green test in tests/BuyersSolutionTests/).

Tests: red/green map

test project against expected at start after the refactoring
tests/BuyersTests/ src/Buyers/ red or flaky (surge pricing) — that is the point green once you stub with a pure lambda
tests/BuyersSolutionTests/ solution/Buyers/ green —
tests/SliceWithLambdaTests/ solution/SliceWithLambda/ green — (the demo end states, with pure-lambda stubs, incl. a throwing stub)

Run

dotnet test                                    # everything
dotnet test tests/BuyersTests                  # the pair exercise only

Next move (debrief)

Add DiscountFor(IceCream) to PriceList and feel why one lambda is no longer enough — extract a role interface (IPricing) instead of two lambdas.