Modern C# · Lesson 42

C# 14 inferred lambda parameter modifiers

Keep a real out-parameter delegate while omitting redundant types externally.

External .NET 10 SDK / stable C# 14. This project does not run in SharpRune's .NET 9 playground.

Read or download the complete SDK project, then follow its setup on your computer. The separate C# companion practice below runs in SharpRune’s .NET 9 console environment.

The concept and environment

C# 14 permits parameter modifiers in an implicitly typed lambda parameter list, when the target delegate supplies the parameter types. The external TryRead lambda keeps out but omits the redundant string and integer type names. The portable .NET 9 companion uses the previously supported fully typed form. Both call the real integer parsing API with the same invariant, digits-only policy. This is a source-language simplification, not dynamic typing and not removal of the delegate's by-reference contract. Only the supplied external .NET 10 project compiles the new parameter-list syntax.

Walk through the portable program

TryRead accepts input text and assigns an integer result through an out parameter. Parsing twelve succeeds and stores twelve, while parsing x fails and the API supplies zero for its output. The booleans are retained separately so the final report can print both success and assigned value for each call. Output is valid:True:12 and invalid:False:0. The zero on failure is the documented behavior of this parsing API; the out modifier alone does not imply that every user-defined failing operation must assign zero rather than another specified value.

Compile the genuine external feature

The real external lambda is written with text and out value, and its TryRead target declares their actual types. The compiler checks parameter modifiers against that target and ensures the delegate shape is compatible. Removing type names does not permit reading an unassigned out value or returning without assigning a required result. The net10.0 project uses language version 14.0 explicitly. Its expected output matches the fully typed companion, but compilation exercises a genuine new syntax rule rather than converting the lambda into a string or interpreting modifiers through custom runtime code.

Pitfalls and limits

Do not remove the target and assume an otherwise untyped lambda can invent parameter types from arbitrary usage. Context is what makes this concise form meaningful. The params modifier still requires an explicitly typed parameter list, so not every parameter-related keyword follows the same shortcut. Keep invocation modifiers correct as well: callers still pass out for the delegate's output argument. Finally, parsing policy remains independent of syntax. NumberStyles.None deliberately rejects signs and surrounding whitespace here; a more permissive ingestion contract needs explicit changes and new expected cases, not merely a shorter lambda declaration.

Practice one: isolate a contract

First parse zero and predict success with an output of zero, then compare that with failure for x, which also outputs zero. Hint: the boolean and assigned value form the result contract together; the integer alone cannot distinguish these cases. Add space-padded twelve and predict failure under the current NumberStyles.None policy. Keep the same cases in the portable and external programs so the exercise checks the parser's actual rule while each compiler separately handles its own lambda syntax.

Practice two: challenge an assumption

For another exercise, define a small TryRead-like delegate for a different bounded domain and implement it without relying on integer parsing. Assign its out result on every path and write distinct success and failure expectations. Hint: choose the failure value deliberately and document it instead of assuming the language supplies a default automatically. Compile the external version with inferred parameter types and the portable version with explicit types. Then intentionally mismatch a modifier in a separate compile experiment and observe a compiler diagnostic, keeping invalid code out of the runnable baseline.

Review the resulting contract

A lambda review should identify its target delegate, parameter types, modifiers, assignment obligations, and the domain policy inside its body. The new syntax removes repetition only where that information is already available to the compiler. The sample retains deterministic parsing and explicit output observations so a concise declaration cannot hide the distinction between successful zero and failed conversion. Use the external project to verify C# 14 acceptance, and use the portable companion to inspect the same small behavior within SharpRune's supported environment without claiming the browser has upgraded its language version.

External .NET 10 SDK / stable C# 14. This project does not run in SharpRune's .NET 9 playground.

C# 14 inferred lambda parameter modifiers lab

Set up the SDK project

  1. Save every listed file at its exact relative path in a fresh directory.
  2. Install a .NET 10 SDK supporting stable C# 14; check dotnet --version.
  3. Run dotnet run --project FeatureLab.csproj. Keep this external code separate from the .NET 9 playground.

Download complete SDK project ↗

Project files

Program.csDownload file ↗
using System.Globalization;
TryRead parse = (text, out value) => int.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out value);
bool valid = parse("12", out int first);
bool invalid = parse("x", out int second);
Console.WriteLine($"valid:{valid}:{first}");
Console.WriteLine($"invalid:{invalid}:{second}");
delegate bool TryRead(string text, out int value);
FeatureLab.csprojDownload file ↗
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <LangVersion>14.0</LangVersion>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>

Expected result

Expected console output, in this order: valid:True:12 invalid:False:0 The portable companion compares the same small rule using older syntax; it does not compile or emulate the new feature.

Use the listed SDK and dependencies for this project. Open C# practice runs the separate portable exercise.

C# companion practice

using System;
using System.Globalization;
TryRead parse = (string text, out int value) => int.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out value);
bool valid = parse("12", out int first);
bool invalid = parse("x", out int second);
Console.WriteLine($"valid:{valid}:{first}");
Console.WriteLine($"invalid:{invalid}:{second}");
delegate bool TryRead(string text, out int value);
Open C# practice

C# practice output

valid:True:12
invalid:False:0
Try this
Retain the delegate target when removing lambda parameter types externally. The out modifier still supplies a real by-reference result.

Further reading