Skip to content

Commit 854ccdc

Browse files
committed
Add docs updates for simpler lambda parameter modifiers
Fixes #44887 Publish the speclet and update the language reference to allow modifiers on implicitly typed lambda expressions.
1 parent c179223 commit 854ccdc

File tree

6 files changed

+47
-18
lines changed

6 files changed

+47
-18
lines changed

docfx.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@
506506
"_csharplang/proposals/csharp-11.0/*.md": "09/30/2022",
507507
"_csharplang/proposals/csharp-12.0/*.md": "08/15/2023",
508508
"_csharplang/proposals/csharp-13.0/*.md": "10/31/2024",
509-
"_csharplang/proposals/*.md": "10/31/2024",
509+
"_csharplang/proposals/*.md": "02/14/2025",
510510
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "11/08/2022",
511511
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "11/08/2023",
512512
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "11/09/2024",
@@ -684,6 +684,7 @@
684684
"_csharplang/proposals/field-keyword.md": "The `field` contextual keyword",
685685
"_csharplang/proposals/unbound-generic-types-in-nameof.md": "Unbound generic types in `nameof`",
686686
"_csharplang/proposals/first-class-span-types.md": "First-class span types",
687+
"_csharplang/proposals/simple-lambda-parameters-with-modifiers.md": "Simple lambda parameters with modifiers",
687688
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "C# compiler breaking changes since C# 10",
688689
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "C# compiler breaking changes since C# 11",
689690
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "C# compiler breaking changes since C# 12",
@@ -807,6 +808,7 @@
807808
"_csharplang/proposals/field-keyword.md": "This proposal introduces a new keyword, `field`, that accesses the compiler generated backing field in a property accessor.",
808809
"_csharplang/proposals/unbound-generic-types-in-nameof.md": "This proposal introduces the ability to use unbound generic types such as `List<>` in `nameof` expressions. The type argument isn't required.",
809810
"_csharplang/proposals/first-class-span-types.md": "This proposal provides several implicit conversions to `Span<T>` and `ReadOnlySpan<T>` that enable library authors to have fewer overloads and developers to write code that resolves to faster Span based APIs",
811+
"_csharplang/proposals/simle-lambda-parameters-with-modifiers.md": "This proposal provides allows lambda parmaeters to be declared with modifiers without requiring their type names. You can add modifiers like `ref` and `out` to lambda parameters without specifying their type.",
810812
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "Learn about any breaking changes since the initial release of C# 10 and included in C# 11",
811813
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "Learn about any breaking changes since the initial release of C# 11 and included in C# 12",
812814
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "Learn about any breaking changes since the initial release of C# 12 and included in C# 13",

docs/csharp/language-reference/compiler-messages/params-arrays.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ f1_keywords:
1717
- "CS9225"
1818
- "CS9227"
1919
- "CS9228"
20+
- "CS9272"
2021
helpviewer_keywords:
2122
- "CS0225"
2223
- "CS0231"
@@ -33,7 +34,8 @@ helpviewer_keywords:
3334
- "CS9225"
3435
- "CS9227"
3536
- "CS9228"
36-
ms.date: 05/20/2024
37+
- "CS9272"
38+
ms.date: 02/13/2025
3739
---
3840
# Errors and warnings related to the `params` modifier on method parameters
3941

@@ -57,6 +59,7 @@ That's by design. The text closely matches the text of the compiler error / warn
5759
- [**CS9225**](#other-params-errors): *Constructor leaves required member uninitialized.*
5860
- [**CS9227**](#parameter-and-argument-type-rules): *Type does not contain a definition for a suitable instance `Add` method.*
5961
- [**CS9228**](#parameter-and-argument-type-rules): *Non-array params collection type must have an applicable constructor that can be called with no arguments.*
62+
- [**CS9272**](#other-params-errors): *Implicitly typed lambda parameter cannot have the 'params' modifier.*
6063

6164
## Method declaration rules
6265

@@ -101,6 +104,7 @@ The following errors indicate other issues with using the `params` modifier:
101104
- **CS9223**: *Creation of params collection results in an infinite chain of invocation of constructor.*
102105
- **CS9224**: *Method cannot be less visible than the member with params collection.*
103106
- **CS9225**: *Constructor leaves required member uninitialized.*
107+
- **CS9272**: *Implicitly typed lambda parameter cannot have the 'params' modifier.*
104108

105109
A method that implements an interface must include the `params` modifier if and only if the interface member has the `params` modifier. Similarly, either both declarations of a `partial` method must include the `params` modifier, or none can include the `params` modifier.
106110

@@ -111,6 +115,7 @@ The compiler generates one of the final three errors in the preceding list when
111115
- The compiler emits **CS9223** when the code emitted to create the collection also contains a params collection of the same type. Typically, the `Add` method takes a `params` collection of the same type.
112116
- The compiler emits **CS9224** when the `Create` method for the collection type is less accessible than the method that takes the `params` parameter of the collection type.
113117
- The compiler emits **CS9225** when the collection type has a required member and the parameterless constructor doesn't initialize that member and have the <xref:System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute?displayProperty=nameWithType> on the parameterless constructor.
118+
- The compiler emits **CS9272** when you've used the `params` modifier without type information on a lambda expression. You must specify the types for all lambda expression parameters to use the `params` modifier.
114119

115120
## See also
116121

docs/csharp/language-reference/operators/lambda-expressions.md

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "Lambda expressions - Lambda expressions and anonymous functions"
33
description: C# lambda expressions that are used to create anonymous functions and expression bodied members.
4-
ms.date: 11/22/2024
4+
ms.date: 02/13/2025
55
helpviewer_keywords:
66
- "lambda expressions [C#]"
77
- "outer variables [C#]"
@@ -49,7 +49,7 @@ A lambda expression with an expression on the right side of the `=>` operator is
4949
(input-parameters) => expression
5050
```
5151

52-
The body of an expression lambda can consist of a method call. However, if you're creating [expression trees](../../advanced-topics/expression-trees/index.md) that are evaluated outside the context of the .NET Common Language Runtime (CLR), such as in SQL Server, you shouldn't use method calls in lambda expressions. The methods have no meaning outside the context of the .NET Common Language Runtime (CLR).
52+
The body of an expression lambda can consist of a method call. However, when creating [expression trees](../../advanced-topics/expression-trees/index.md) evaluated by a query provider, limit method calls to those methods recognized by the query provider. Otherwise, the query provider can't replicate the method's function.
5353

5454
## Statement lambdas
5555

@@ -79,11 +79,11 @@ Two or more input parameters are separated by commas:
7979

8080
:::code language="csharp" source="snippets/lambda-expressions/GeneralExamples.cs" id="SnippetTwoParameters":::
8181

82-
Sometimes the compiler can't infer the types of input parameters. You can specify the types explicitly as shown in the following example:
82+
The compiler typically infers the types for parameters to lambda expressions, referred to as an *implicitly typed parameter list*. You can specify the types explicitly, referred to as an *explicitly typed parameter list*. An explicitly typed parameter list is shown in the following example. :
8383

8484
:::code language="csharp" source="snippets/lambda-expressions/GeneralExamples.cs" id="SnippetExplicitlyTypedParameters":::
8585

86-
Input parameter types must be all explicit or all implicit; otherwise, a [CS0748](../compiler-messages/lambda-expression-errors.md#lambda-expression-parameters-and-returns) compiler error occurs.
86+
Input parameter types must be all explicit or all implicit; otherwise, a [CS0748](../compiler-messages/lambda-expression-errors.md#lambda-expression-parameters-and-returns) compiler error occurs. Before C# 14, you must include the explicit type on a parameter if it has any modifiers, such as `ref` or `out`. In C# 14, that restriction is removed. However, you must still declare the type if you use the `params` modifier.
8787

8888
You can use [discards](../../fundamentals/functional/discards.md) to specify two or more input parameters of a lambda expression that aren't used in the expression:
8989

@@ -92,13 +92,13 @@ You can use [discards](../../fundamentals/functional/discards.md) to specify two
9292
Lambda discard parameters can be useful when you use a lambda expression to [provide an event handler](../../programming-guide/events/how-to-subscribe-to-and-unsubscribe-from-events.md).
9393

9494
> [!NOTE]
95-
> For backwards compatibility, if only a single input parameter is named `_`, then, within a lambda expression, `_` is treated as the name of that parameter.
95+
> For backwards compatibility, if only a single input parameter is named `_`, `_` is treated as the name of that parameter within that lambda expression.
9696

97-
Beginning with C# 12, you can provide *default values* for parameters on lambda expressions. The syntax and the restrictions on default parameter values are the same as for methods and local functions. The following example declares a lambda expression with a default parameter, then calls it once using the default and once with two explicit parameters:
97+
Beginning with C# 12, you can provide *default values* for explicitly typed parameter lists. The syntax and the restrictions on default parameter values are the same as for methods and local functions. The following example declares a lambda expression with a default parameter, then calls it once using the default and once with two explicit parameters:
9898

9999
:::code language="csharp" source="snippets/lambda-expressions/GeneralExamples.cs" id="SnippetDefaultParameters":::
100100

101-
You can also declare lambda expressions with `params` arrays or collections as parameters:
101+
You can also declare lambda expressions with `params` arrays or collections as the last parameter in an explicitly typed parameter list:
102102

103103
:::code language="csharp" source="snippets/lambda-expressions/GeneralExamples.cs" id="SnippetParamsArray":::
104104

@@ -166,7 +166,7 @@ For more information about how to create and use async methods, see [Asynchronou
166166

167167
## Lambda expressions and tuples
168168

169-
The C# language provides built-in support for [tuples](../builtin-types/value-tuples.md). You can provide a tuple as an argument to a lambda expression, and your lambda expression can also return a tuple. In some cases, the C# compiler uses type inference to determine the types of tuple components.
169+
The C# language provides built-in support for [tuples](../builtin-types/value-tuples.md). You can provide a tuple as an argument to a lambda expression, and your lambda expression can also return a tuple. In some cases, the C# compiler uses type inference to determine the types of tuple elements.
170170

171171
You define a tuple by enclosing a comma-delimited list of its components in parentheses. The following example uses tuple with three components to pass a sequence of numbers to a lambda expression, which doubles each value and returns a tuple with three components that contains the result of the multiplications.
172172

@@ -299,7 +299,7 @@ var inc = [return: NotNullIfNotNull(nameof(s))] (int? s) => s.HasValue ? s++ : n
299299
As the preceding examples show, you must parenthesize the input parameters when you add attributes to a lambda expression or its parameters.
300300

301301
> [!IMPORTANT]
302-
> Lambda expressions are invoked through the underlying delegate type. That is different than methods and local functions. The delegate's `Invoke` method doesn't check attributes on the lambda expression. Attributes don't have any effect when the lambda expression is invoked. Attributes on lambda expressions are useful for code analysis, and can be discovered via reflection. One consequence of this decision is that the <xref:System.Diagnostics.ConditionalAttribute?displayProperty=nameWithType> cannot be applied to a lambda expression.
302+
> Lambda expressions are invoked through the underlying delegate type. That invocation is different than methods and local functions. The delegate's `Invoke` method doesn't check attributes on the lambda expression. Attributes don't have any effect when the lambda expression is invoked. Attributes on lambda expressions are useful for code analysis, and can be discovered via reflection. One consequence of this decision is that the <xref:System.Diagnostics.ConditionalAttribute?displayProperty=nameWithType> can't be applied to a lambda expression.
303303
304304
## Capture of outer variables and variable scope in lambda expressions
305305

@@ -309,7 +309,7 @@ Lambdas can refer to *outer variables*. These *outer variables* are the variable
309309

310310
The following rules apply to variable scope in lambda expressions:
311311

312-
- A variable that is captured won't be garbage-collected until the delegate that references it becomes eligible for garbage collection.
312+
- A variable that is captured isn't garbage-collected until the delegate that references it becomes eligible for garbage collection.
313313
- Variables introduced within a lambda expression aren't visible in the enclosing method.
314314
- A lambda expression can't directly capture an [in](../keywords/method-parameters.md#in-parameter-modifier), [ref](../keywords/ref.md), or [out](../keywords/method-parameters.md#out-parameter-modifier) parameter from the enclosing method.
315315
- A [return](../statements/jump-statements.md#the-return-statement) statement in a lambda expression doesn't cause the enclosing method to return.

docs/csharp/language-reference/operators/lambda-operator.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "The lambda operator - The `=>` operator is used to define a lambda expression"
33
description: "The C# => operator defines lambda expressions and expression bodied members. Lambda expressions define a block of code used as data."
4-
ms.date: 11/28/2022
4+
ms.date: 02/13/2025
55
f1_keywords:
66
- "=>_CSharpKeyword"
77
helpviewer_keywords:
@@ -19,15 +19,15 @@ In [lambda expressions](lambda-expressions.md), the lambda operator `=>` separat
1919

2020
The following example uses the [LINQ](/dotnet/csharp/linq/) feature with method syntax to demonstrate the usage of lambda expressions:
2121

22-
[!code-csharp-interactive[infer types of input variables](snippets/shared/LambdaOperator.cs#InferredTypes)]
22+
:::code language="csharp" interactive="try-dotnet-method" source="snippets/shared/LambdaOperator.cs" id="InferredTypes":::
2323

24-
Input parameters of a lambda expression are strongly typed at compile time. When the compiler can infer the types of input parameters, like in the preceding example, you may omit type declarations. If you need to specify the type of input parameters, you must do that for each parameter, as the following example shows:
24+
Input parameters of a lambda expression are strongly typed at compile time. When the compiler can infer the types of input parameters, like in the preceding example, you can omit type declarations. If you need to specify the type of input parameters, you must do that for each parameter, as the following example shows:
2525

26-
[!code-csharp-interactive[specify types of input variables](snippets/shared/LambdaOperator.cs#ExplicitTypes)]
26+
:::code language="csharp" interactive="try-dotnet-method" source="snippets/shared/LambdaOperator.cs" id="ExplicitTypes":::
2727

2828
The following example shows how to define a lambda expression without input parameters:
2929

30-
[!code-csharp-interactive[without input variables](snippets/shared/LambdaOperator.cs#WithoutInput)]
30+
:::code language="csharp" interactive="try-dotnet-method" source="snippets/shared/LambdaOperator.cs" id="WithoutInput":::
3131

3232
For more information, see [Lambda expressions](lambda-expressions.md).
3333

@@ -39,7 +39,7 @@ An expression body definition has the following general syntax:
3939
member => expression;
4040
```
4141

42-
where `expression` is a valid expression. The return type of `expression` must be implicitly convertible to the member's return type. If the member:
42+
Where `expression` is a valid expression. The return type of `expression` must be implicitly convertible to the member's return type. If the member:
4343

4444
- Has a `void` return type or
4545
- Is a:

docs/csharp/specification/toc.yml

+2
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ items:
159159
href: ../../../_csharplang/proposals/csharp-13.0/method-group-natural-type-improvements.md
160160
- name: Optional Lambda expression parameters
161161
href: ../../../_csharplang/proposals/csharp-12.0/lambda-method-group-defaults.md
162+
- name: Simple lambda parameters with modifiers
163+
href: ../../../_csharplang/proposals/lambda-parameters-with-modifiers.md
162164
- name: Checked user-defined operators
163165
href: ../../../_csharplang/proposals/csharp-11.0/checked-user-defined-operators.md
164166
- name: Unsigned right-shift operator

docs/csharp/whats-new/csharp-14.md

+20
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,24 @@ You can find the list of implicit span conversions in the article on [built-in t
4444

4545
Beginning with C# 14, the argument to `nameof` can be an unbound generic type. For example, `nameof(List<>)` evaluates to `List`. In earlier versions of C#, only closed generic types, such as `List<int>`, could be used to produce `List`.
4646

47+
## Simple lambda parameters with modifiers
48+
49+
Beginning with C# 14, you can add parameter modifiers, such as `scoped`, `ref`, `in`, `out`, or `ref readonly` to lambda expression parameters without specifying the parameter type:
50+
51+
```csharp
52+
delegate bool TryParse<T>(string text, out T result);
53+
// ...
54+
TryParse<int> parse1 = (text, out result) => Int32.TryParse(text, out result);
55+
```
56+
57+
Previously, adding any modifiers was allowed only when the parameter declarations included the types for the parameters. The preceding declaration would require typs on all parameters:
58+
59+
```csharp
60+
TryParse<int> parse2 = (string text, out int result) => Int32.TryParse(text, out result);
61+
```
62+
63+
The `params` modifier still requires an explicitly typed parameter list.
64+
65+
You can read more about these changes in the article on [lambda expressions](../language-reference/operators/lambda-expressions.md#input-parameters-of-a-lambda-expression) in the C# language reference.
66+
4767
<!-- Add link to What's new in .NET 10 once it's published -->

0 commit comments

Comments
 (0)