# Slice with Lambda — C# exercises Exercises for the **Slice with Lambda** learning hour (C# variant). - Condensed mechanics: (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 readB = a => ...` | `int c = a + b; return c * 2;` | | `WeatherStation.cs` | `if (_wateredToday)` | `_lightSensor.Read()` (byte) | `Func readLight = () => ...` | sunny -> `WateringDuration.Medium`, shady -> `Short` (reading > 128 => `SunExposure.Sunny`) | | `IceCreamShop.cs` | `if (_thawedToday)` | `_thermometer.Read()` (byte) | `Func 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`) 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 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 ```sh 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.