Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .openpublishing.redirection.json
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,10 @@
"source_path": "docs/csharp/getting-started/with-visual-studio.md",
"redirect_url": "/dotnet/core/tutorials/with-visual-studio"
},
{
"source_path": "docs/csharp/implicitly-typed-lambda-expressions.md",
"redirect_url": "/dotnet/csharp/programming-guide/statements-expressions-operators"
},
{
"source_path": "docs/csharp/interactive-with-bash.md",
"redirect_url": "/dotnet/csharp/index"
Expand Down Expand Up @@ -1065,6 +1069,10 @@
"source_path": "docs/csharp/language-reference/operators/xor-operator.md",
"redirect_url": "/dotnet/csharp/language-reference/operators/boolean-logical-operators#logical-exclusive-or-operator-"
},
{
"source_path": "docs/csharp/local-functions-vs-lambdas.md",
"redirect_url": "/dotnet/csharp/programming-guide/classes-and-structs/local-functions"
},
{
"source_path": "docs/csharp/methods-lambda-expressions.md",
"redirect_url": "/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions"
Expand Down Expand Up @@ -3745,6 +3753,10 @@
"source_path": "docs/standard/serialization/add-element-for-xmlschemaimporterextensions.md",
"redirect_url": "/dotnet/standard/serialization/add-element-for-schemaimporterextensions"
},
{
"source_path": "docs/standard/serialization/custom-serialization-order-with-xmlserializer.md",
"redirect_url": "/dotnet/standard/serialization/xml-and-soap-serialization"
},
{
"source_path": "docs/standard/serialization/marshal-by-value.md",
"redirect_url": "/dotnet/standard/serialization/serialization-concepts"
Expand All @@ -3753,6 +3765,18 @@
"source_path": "docs/standard/serialization/persistent-storage.md",
"redirect_url": "/dotnet/standard/serialization/serialization-concepts"
},
{
"source_path": "docs/standard/serialization/samples-binary.md",
"redirect_url": "/dotnet/standard/serialization/basic-serialization-technology-sample"
},
{
"source_path": "docs/standard/serialization/samples-xml.md",
"redirect_url": "/dotnet/standard/serialization/xml-and-soap-serialization"
},
{
"source_path": "docs/standard/serialization/schemaimporterextension-technology-sample.md",
"redirect_url": "/dotnet/standard/serialization/basic-serialization-technology-sample"
},
{
"source_path": "docs/standard/serialization/serialization-how-to-topics.md",
"redirect_url": "/dotnet/standard/serialization/how-to-serialize-an-object"
Expand All @@ -3765,6 +3789,14 @@
"source_path": "docs/standard/serialization/serialization-tools.md",
"redirect_url": "/dotnet/standard/serialization/xml-serializer-generator-tool-sgen-exe"
},
{
"source_path": "docs/standard/serialization/version-tolerant-serialization-technology-sample.md",
"redirect_url": "/dotnet/standard/serialization/basic-serialization-technology-sample"
},
{
"source_path": "docs/standard/serialization/web-services-generics-serialization-technology-sample.md",
"redirect_url": "/dotnet/standard/serialization/basic-serialization-technology-sample"
},
{
"source_path": "docs/standard/serialization/web-services-ixmlserializable-technology-sample.md",
"redirect_url": "https://docs.microsoft.com/previous-versions/dotnet/netframework-4.0/h2byscsb(v=vs.100)"
Expand Down
28 changes: 0 additions & 28 deletions docs/csharp/implicitly-typed-lambda-expressions.md

This file was deleted.

110 changes: 0 additions & 110 deletions docs/csharp/local-functions-vs-lambdas.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Starting with C# 7.0, C# supports *local functions*. Local functions are private
However, local functions can't be declared inside an expression-bodied member.

> [!NOTE]
> In some cases, you can use a lambda expression to implement functionality also supported by a local function. For a comparison, see [Local functions compared to Lambda expressions](../../local-functions-vs-lambdas.md).
> In some cases, you can use a lambda expression to implement functionality also supported by a local function. For a comparison, see [Local functions vs. lambda expressions](#local-functions-vs-lambda-expressions).

Local functions make the intent of your code clear. Anyone reading your code can see that the method is not callable except by the containing method. For team projects, they also make it impossible for another developer to mistakenly call the method directly from elsewhere in the class or struct.

Expand Down Expand Up @@ -69,6 +69,59 @@ As we did with the method iterator, we can refactor the code from this example t

[!code-csharp[LocalFunctionAsync](~/samples/snippets/csharp/programming-guide/classes-and-structs/local-functions-async2.cs)]

## Local functions vs. lambda expressions

At first glance, local functions and [lambda expressions](../statements-expressions-operators/lambda-expressions.md) are very similar. In many cases, the choice between using lambda expressions and local functions is a matter of style and personal preference. However, there are real differences in where you can use one or the other that you should be aware of.

Let's examine the differences between the local function and lambda expression implementations of the factorial algorithm. First the version using a local function:

[!code-csharp[LocalFunctionFactorial](../../../../samples/snippets/csharp/new-in-7/MathUtilities.cs#37_LocalFunctionFactorial "Recursive factorial using local function")]

Contrast that implementation with a version that uses lambda expressions:

[!code-csharp[26_LambdaFactorial](../../../../samples/snippets/csharp/new-in-7/MathUtilities.cs#38_LambdaFactorial "Recursive factorial using lambda expressions")]

The local functions have names. The lambda expressions are anonymous methods that are assigned to variables that are `Func` or `Action` types. When you
declare a local function, the argument types and return type are part of the function declaration. Instead of being part of the body of the lambda expression, the argument types and return type are part of the lambda expression's variable type declaration. Those two differences may result in clearer code.

Local functions have different rules for definite assignment than lambda expressions. A local function declaration can be referenced from any code location where it is in scope. A lambda expression must be assigned to a delegate variable before it can be accessed (or called through the delegate referencing the lambda expression). Notice that the version using the lambda expression must declare and initialize the lambda expression `nthFactorial` before defining it. Not doing so results in a compile time error for referencing `nthFactorial` before assigning it. These differences mean that recursive algorithms are easier to create using local functions. You can declare and define a local function that calls itself. Lambda expressions must be declared, and assigned a default value before they can be re-assigned to a body that references the same lambda expression.

Definite assignment rules also affect any variables that are captured by the local function or lambda expression. Both local functions and lambda expression rules demand that any captured variables are definitely assigned at the point when the local function or lambda expression is converted to a delegate. The difference is that lambda expressions are converted to delegates when they are declared. Local functions are converted to delegates only when used as a delegate. If you declare a local function and only reference it by calling it like a method, it will not be converted to a delegate. That rule enables you to declare a local function at any convenient location in its enclosing scope. It's common to declare local functions at the end of the parent method, after any return statements.

Third, the compiler can perform static analysis that enables local functions to definitely assign captured variables in the enclosing scope. Consider this example:

```csharp
int M()
{
int y;
LocalFunction();
return y;

void LocalFunction() => y = 0;
}
```

The compiler can determine that `LocalFunction` definitely assigns `y` when called. Because `LocalFunction` is called before the `return` statement, `y` is definitely assigned at the `return` statement.

The analysis that enables the example analysis enables the fourth difference. Depending on their use, local functions can avoid heap allocations that are always necessary for lambda expressions. If a local function is never converted to a delegate, and none of the variables captured by the local function is captured by other lambdas or local functions that are converted to delegates, the compiler can avoid heap allocations.

Consider this async example:

[!code-csharp[TaskLambdaExample](../../../../samples/snippets/csharp/new-in-7/AsyncWork.cs#36_TaskLambdaExample "Task returning method with lambda expression")]

The closure for this lambda expression contains the `address`, `index` and `name` variables. In the case of local functions, the object that implements the closure may be a `struct` type. That struct type would be passed by reference to the local function. This difference in implementation would save on an allocation.

The instantiation necessary for lambda expressions means extra memory allocations, which may be a performance factor in time-critical code paths. Local functions do not incur this overhead. In the example above, the local functions version has 2 fewer allocations than the lambda expression version.

> [!NOTE]
> The local function equivalent of this method also uses a class for the closure. Whether the closure for a local function is implemented as a `class` or a `struct` is an implementation detail. A local function may use a `struct` whereas a lambda will always use a `class`.

[!code-csharp[TaskLocalFunctionExample](../../../../samples/snippets/csharp/new-in-7/AsyncWork.cs#TaskExample "Task returning method with local function")]

One final advantage not demonstrated in this sample is that local functions can be implemented as iterators, using the `yield return` syntax to produce a sequence of values. The `yield return` statement is not allowed in lambda expressions.

While local functions may seem redundant to lambda expressions, they actually serve different purposes and have different uses. Local functions are more efficient for the case when you want to write a function that is called only from the context of another method.

## See also

- [Methods](methods.md)
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ For more information, see the [Anonymous function expressions](~/_csharplang/spe
- [C# Programming Guide](../index.md)
- [LINQ (Language-Integrated Query)](../concepts/linq/index.md)
- [Expression Trees](../concepts/expression-trees/index.md)
- [Local functions compared to lambda expressions](../../local-functions-vs-lambdas.md)
- [Local functions vs. lambda expressions](../classes-and-structs/local-functions.md#local-functions-vs-lambda-expressions)
- [Implicitly typed lambda expressions](../../implicitly-typed-lambda-expressions.md)
- [Visual Studio 2008 C# Samples (see LINQ Sample Queries files and XQuery program)](https://code.msdn.microsoft.com/Visual-Studio-2008-C-d295cdba)
- [Recursive lambda expressions](https://docs.microsoft.com/archive/blogs/madst/recursive-lambda-expressions)
2 changes: 2 additions & 0 deletions docs/csharp/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1219,6 +1219,8 @@
href: programming-guide/xmldoc/exception.md
- name: <include>
href: programming-guide/xmldoc/include.md
- name: <inheritdoc>
href: programming-guide/xmldoc/inheritdoc.md
- name: <list>
href: programming-guide/xmldoc/list.md
- name: <para>
Expand Down
3 changes: 1 addition & 2 deletions docs/csharp/whats-new/csharp-7.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,7 @@ work begins:
[!code-csharp[TaskExample](~/samples/snippets/csharp/new-in-7/AsyncWork.cs#TaskExample "Task returning method with local function")]

> [!NOTE]
> Some of the designs that are supported by local functions
> could also be accomplished using *lambda expressions*. For more information, see [Local functions vs. lambda expressions](../local-functions-vs-lambdas.md).
> Some of the designs that are supported by local functions can also be accomplished using *lambda expressions*. For more information, see [Local functions vs. lambda expressions](../programming-guide/classes-and-structs/local-functions.md#local-functions-vs-lambda-expressions).

## More expression-bodied members

Expand Down
Loading