- 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
Slice with Lambda — C# exercises
Exercises for the Slice with Lambda learning hour (C# variant).
- Condensed mechanics: https://www.sammancoaching.org/refactorings/slice.html (where that page says mock, we use a lambda instead).
- Full Java walkthrough:
../../ideas/slice-with-lambda/storyboard/storyboard.md.
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:
- 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). - Extract the loop into a method taking the lambda as a parameter.
- Keep a no-arg overload for callers (pass the method group
priceList.PriceForas 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.