Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New in csharp7 #1188

Merged
merged 5 commits into from
Nov 13, 2016
Merged
Show file tree
Hide file tree
Changes from 4 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
63 changes: 63 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary

###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
519 changes: 493 additions & 26 deletions docs/csharp/csharp-7.md

Large diffs are not rendered by default.

58 changes: 58 additions & 0 deletions docs/csharp/local-functions-vs-lambdas.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
title: Local functions vs. lambda expressions
description: Why Local Functions might be a better choice than Lambda Expressions
keywords: C#, .NET, .NET Core, Latest Features, What's New, local functions, lambda expressions
author: BillWagner
ms.author: wiwagn
manager: wpickett
ms.date: 10/27/2016
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add ms.author:

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

ms.topic: article
ms.prod: visual-studio-dev-15
ms.technology: devlang-csharp
ms.devlang: csharp
ms.assetid: 368d1752-3659-489a-97b4-f15d87e49ae3
---

### Local functions compared to Lambda expressions

In some use cases, you could create a lambda expression and use it
without needing to create a local function. Here's an example async
method that does just that:

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

However, there are a number of reasons to prefer using local functions
instead of defining and calling lambda expressions.

First, for lambda expressions, the compiler must create an anonymous class
and an instance of that class to store any variables captured by the
closure. The closure for this lambda expression contains the `address`,
`index` and `name` variables.

Second, lambda expressions are implemented by instantiating a delegate
and invoking that delegate. Local functions are implemented as method calls.
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.

Third, local functions can be called before they are defined. Lambda
expressions must be declared before they are defined. This
means local functions are easier to use in recursive algorithms:

[!code-csharp[LocalFunctionFactorial](../../samples/snippets/csharp/new-in-7/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/new-in-7/MathUtilities.cs#38_LambdaFactorial "Recursive factorial using lambda expressions")]

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.
Recursive algorithms are easier to create using local functions.

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.

7 changes: 7 additions & 0 deletions docs/csharp/pattern-matching.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,10 @@ ms.assetid: 1e575c32-2e2b-4425-9dca-7d118f3ed15b
>
> Learn more about how you can contribute on [GitHub](https://github.com/dotnet/docs/blob/master/CONTRIBUTING.md).
>

<!--

<< Build the sample in an additive way >>
<< Do the shape thing for the https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/discriminated-unions shapes >>

-->
2 changes: 1 addition & 1 deletion docs/csharp/tour-of-csharp/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ The following summarizes C#’s operators, listing the operator categories in or
* Logical XOR
- `x ^ y`: Integer bitwise XOR, boolean logical XOR
* Logical OR
`x | y`: Integer bitwise OR, boolean logical OR
- `x | y`: Integer bitwise OR, boolean logical OR
* Conditional AND
- `x && y`: Evaluates `y` only if `x` is not `false`
* Conditional OR
Expand Down
14 changes: 14 additions & 0 deletions docs/csharp/tuples.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,17 @@ ms.assetid: ee8bf7c3-aa3e-4c9e-a5c6-e05cc6138baa
>
> Learn more about how you can contribute on [GitHub](https://github.com/dotnet/docs/blob/master/CONTRIBUTING.md).
>

<!--

Ideas while writing the main C# 7 article:

. Go into detail on the 'shape' of a tuple and assignment between Tuples

. Go into detail on deconstruction methods

. Consider examples where a LINQ query returns a subset of the columns for a database record.


-->

2 changes: 1 addition & 1 deletion docs/toc.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
### [Delegates](csharp/tour-of-csharp/delegates.md)
### [Attributes](csharp/tour-of-csharp/attributes.md)
## [Latest features](csharp/whats-new.md)
### [🔧 What's new in C# 7](csharp/csharp-7.md)
### [What's new in C# 7](csharp/csharp-7.md)
### [What's new in C# 6](csharp/csharp-6.md)
## [🔧 C# Interactive](csharp/interactive.md)
### [🔧 Using C# Interactive in Visual Studio](csharp/interactive-with-visualstudio.md)
Expand Down
22 changes: 22 additions & 0 deletions samples/snippets/csharp/new-in-7/new-in-7.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.25807.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "new-in-7", "new-in-7\new-in-7.csproj", "{63DBE099-DB24-4607-A930-FFEE506CE84C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{63DBE099-DB24-4607-A930-FFEE506CE84C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63DBE099-DB24-4607-A930-FFEE506CE84C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63DBE099-DB24-4607-A930-FFEE506CE84C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63DBE099-DB24-4607-A930-FFEE506CE84C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
6 changes: 6 additions & 0 deletions samples/snippets/csharp/new-in-7/new-in-7/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>
91 changes: 91 additions & 0 deletions samples/snippets/csharp/new-in-7/new-in-7/AsyncWork.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace new_in_7
{
public class AsyncWork
{
#region 29_TaskExample
public Task<string> PerformLongRunningWork(string address, int index, string name)
{
if (string.IsNullOrWhiteSpace(address))
throw new ArgumentException(message: "An address is required", paramName: nameof(address));
if (index < 0)
throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));

return longRunningWorkImplementation();

async Task<string> longRunningWorkImplementation()
{
var interimResult = await FirstWork(address);
var secondResult = await SecondStep(index, name);
return $"The results are {interimResult} and {secondResult}. Enjoy.";
}
}
#endregion

private async Task<string> SecondStep(Int32 index, String name)
{
await Task.Delay(50);
return "This could be a format string";
}

private async Task<int> FirstWork(String address)
{
await Task.Delay(100);
return 42;
}

#region 36_TaskLambdaExample
public Task<string> PerformLongRunningWorkLambda(string address, int index, string name)
{
if (string.IsNullOrWhiteSpace(address))
throw new ArgumentException(message: "An address is required", paramName: nameof(address));
if (index < 0)
throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));

Func<Task<string>> longRunningWorkImplementation = async () =>
{
var interimResult = await FirstWork(address);
var secondResult = await SecondStep(index, name);
return $"The results are {interimResult} and {secondResult}. Enjoy.";
};

return longRunningWorkImplementation();
}
#endregion

#region 30_UsingValueTask
public async ValueTask<int> Func()
{
await Task.Delay(100);
return 5;
}
#endregion

#region 31_AsyncOptimizedValueTask
public ValueTask<int> CachedFunc()
{
return (cache) ? new ValueTask<int>(cacheResult) : new ValueTask<int>(loadCache());
}
private bool cache = false;
private int cacheResult;
private async Task<int> loadCache()
{
// simulate async work:
await Task.Delay(100);
cache = true;
cacheResult = 100;
return cacheResult;
}
#endregion

}
}
68 changes: 68 additions & 0 deletions samples/snippets/csharp/new-in-7/new-in-7/Iterator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace new_in_7
{
public static class Iterator
{
#region 25_IteratorMethod
public static IEnumerable<char> AlphabetSubset(char start, char end)
{
if ((start < 'a') || (start > 'z'))
throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
if ((end < 'a') || (end > 'z'))
throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

if (end <= start)
throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");
for (var c = start; c < end; c++)
yield return c;
}
#endregion

#region 27_IteratorMethodRefactored
public static IEnumerable<char> AlphabetSubset2(char start, char end)
{
if ((start < 'a') || (start > 'z'))
throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
if ((end < 'a') || (end > 'z'))
throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

if (end <= start)
throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");
return alphabetSubsetImplementation(start, end);
}

private static IEnumerable<char> alphabetSubsetImplementation(char start, char end)
{
for (var c = start; c < end; c++)
yield return c;
}
#endregion

#region 28_IteratorMethodLocal
public static IEnumerable<char> AlphabetSubset3(char start, char end)
{
if ((start < 'a') || (start > 'z'))
throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
if ((end < 'a') || (end > 'z'))
throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

if (end <= start)
throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");

return alphabetSubsetImplementation();

IEnumerable<char> alphabetSubsetImplementation()
{
for (var c = start; c < end; c++)
yield return c;
}
}
#endregion

}
}
30 changes: 30 additions & 0 deletions samples/snippets/csharp/new-in-7/new-in-7/MathUtilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace new_in_7
{
public static class MathUtilities
{
#region 37_LocalFunctionFactorial
public static int LocalFunctionFactorial(int n)
{
return nthFactorial(n);
int nthFactorial(int number) => (number < 2) ?
1 : number * nthFactorial(number - 1);
}
#endregion

#region 38_LambdaFactorial
public static int LambdaFactorial(int n)
{
Func<int, int> nthFactorial = default(Func<int, int>);
nthFactorial = (number) => (number < 2) ?
1 : number * nthFactorial(number - 1);
return nthFactorial(n);
}
#endregion
}
}
Loading