C# 7.0 Features
C# 7.0 is the seventh version of C#. This version contains some new features: language support for Tuples, local functions,
out var declarations, digit separators, binary literals, pattern matching, throw expressions,
ref return and
ref local and extended expression bodied members list.
Official reference: What's new in C# 7
The 0b prefix can be used to represent Binary literals.
Binary literals allow constructing numbers from zeroes and ones, which makes seeing which bits are set in the binary representation of a number much easier. This can be useful for working with binary flags.
The following are equivalent ways of specifying an
int with value
34 (=25 + 21):
Before, specifying flag values for an
enum could only be done using one of the three methods in this example:
With binary literals it is more obvious which bits are set, and using them does not require understanding hexadecimal numbers and bitwise arithmetic:
_ may be used as a digit separator. Being able to group digits in large numeric literals has a significant impact on readability.
The underscore may occur anywhere in a numeric literal except as noted below. Different groupings may make sense in different scenarios or with different numeric bases.
Any sequence of digits may be separated by one or more underscores. The
_ is allowed in decimals as well as exponents. The separators have no semantic impact - they are simply ignored.
_ digit separator may not be used:
- at the beginning of the value (
- at the end of the value (
- next to the decimal (
- next to the exponent character (
- next to the type specifier (
- immediately following the
0bin binary and hexadecimal literals (might be changed to allow e.g. 0b_1001_1000)
Extended expression bodied members list
C# 7.0 adds accessors, constructors and finalizers to the list of things that can have expression bodies:
Also see the out var declaration section for the discard operator.
Language support for Tuples
A tuple is an ordered, finite list of elements. Tuples are commonly used in programming as a means to work with one single entity collectively instead of individually working with each of the tuple's elements, and to represent individual rows (ie. "records") in a relational database.
In C# 7.0, methods can have multiple return values. Behind the scenes, the compiler will use the new ValueTuple struct.
Side note: for this to work in Visual Studio 2017, you need to get the
If a tuple-returning method result is assigned to a single variable you can access the members by their defined names on the method signature:
Tuple deconstruction separates a tuple into its parts.
For example, invoking
GetTallies and assigning the return value to two separate variables deconstructs the tuple into those two variables:
var also works:
You can also use shorter syntax, with
var outside of
You can also deconstruct into existing variables:
Swapping is now much simpler (no temp variable needed):
Interestingly, any object can be deconstructed by defining a
Deconstruct method in the class:
In this case, the
(localFirstName, localLastName) = person syntax is invoking
Deconstruct on the
Deconstruction can even be defined in an extension method. This is equivalent to the above:
An alternative approach for the
Person class is to define the
Name itself as a
Tuple. Consider the following:
Then you can instantiate a person like so (where we can take a tuple as an argument):
You can also arbitrarily create tuples in code:
When creating a tuple, you can assign ad-hoc item names to the members of the tuple:
Multiple tuples defined with the same signature (matching types and count) will be inferred as matching types. For example:
stats can be returned since the declaration of the
stats variable and the method's return signature are a match.
Reflection and Tuple Field Names
Member names do not exist at runtime. Reflection will consider tuples with the same number and types of members the same even if member names do not match. Converting a tuple to an
object and then to a tuple with the same member types, but different names, will not cause an exception either.
While the ValueTuple class itself does not preserve information for member names the information is available through reflection in a TupleElementNamesAttribute. This attribute is not applied to the tuple itself but to method parameters, return values, properties and fields. This allows tuple item names to be preserved across assemblies i.e. if a method returns (string name, int count) the names name and count will be available to callers of the method in another assembly because the return value will be marked with TupleElementNameAttribute containing the values "name" and "count".
Use with generics and
The new tuple features (using the underlying
ValueTuple type) fully support generics and can be used as generic type parameter. That makes it possible to use them with the
Use with collections
It may become beneficial to have a collection of tuples in (as an example) a scenario where you're attempting to find a matching tuple based on conditions to avoid code branching.
With the new tuples can become:
Though the naming on the example tuple above is pretty generic, the idea of relevant labels allows for a deeper understanding of what is being attempted in the code over referencing "item1", "item2", and "item3".
Differences between ValueTuple and Tuple
The primary reason for introduction of
ValueTuple is performance.
|Class or structure|
|Mutability (changing values after creation)||mutable||immutable|
|Naming members and other language support||yes||no (TBD)|
- Original Tuples language feature proposal on GitHub
- A runnable VS 15 solution for C# 7.0 features
- NuGet Tuple Package
Local functions are defined within a method and aren't available outside of it. They have access to all local variables and support iterators,
await and lambda syntax. This way, repetitions specific to a function can be functionalized without crowding the class. As a side effect, this improves intellisense suggestion performance.
Local functions considerably simplify code for LINQ operators, where you usually have to separate argument checks from actual logic to make argument checks instant, not delayed until after iteration started.
Local functions also support the
One important thing that you may have noticed is that local functions can be defined under the
return statement, they do not need to be defined above it. Additionally, local functions typically follow the "lowerCamelCase" naming convention as to more easily differentiate themselves from class scope functions.
out var declaration
A common pattern in C# is using
bool TryParse(object input, out object value) to safely parse objects.
out var declaration is a simple feature to improve readability. It allows a variable to be declared at the same time that is it passed as an out parameter.
A variable declared this way is scoped to the remainder of the body at the point in which it is declared.
TryParse prior to C# 7.0, you must declare a variable to receive the value before calling the function:
In C# 7.0, you can inline the declaration of the variable passed to the
out parameter, eliminating the need for a separate variable declaration:
If some of the parameters that a function returns in
out is not needed you can use the discard operator
out var declaration can be used with any existing function which already has
out parameters. The function declaration syntax remains the same, and no additional requirements are needed to make the function compatible with an
out var declaration. This feature is simply syntactic sugar.
Another feature of
out var declaration is that it can be used with anonymous types.
In this code we create a
int key and array of anonymous type value. In the previous version of C# it was impossible to use
TryGetValue method here since it required you to declare the
out variable (which is of anonymous type!). However, with
out var we do not need to explicitly specify the type of the
Note that out var declarations are of limited use in LINQ queries as expressions are interpreted as expression lambda bodies, so the scope of the introduced variables is limited to these lambdas. For example, the following code will not work:
Pattern matching extensions for C# enable many of the benefits of pattern matching from functional languages, but in a way that smoothly integrates with the feel of the underlying language
Pattern matching extends the
switch statement to switch on types:
Pattern matching extends the
is operator to check for a type and declare a new variable at the same time.
can be rewritten as:
Also note that the scope of the pattern variable
s is extended to outside the
if block reaching the end of the enclosing scope, example:
ref return and ref local
Ref returns and ref locals are useful for manipulating and returning references to blocks of memory instead of copying memory without resorting to unsafe pointers.
With this you can pass two values by reference with one of them being returned based on some condition:
Unsafe Ref Operations
System.Runtime.CompilerServices.Unsafe a set of unsafe operations have been defined that allow you to manipulate
ref values as if they were pointers, basically.
For example, reinterpreting a memory address (
ref) as a different type:
Beware of endianness when doing this, though, e.g. check
BitConverter.IsLittleEndian if needed and handle accordingly.
Or iterate over an array in an unsafe manner:
Or the similar
Additionally, one can check if two
ref values are the same i.e. same address:
C# 7.0 allows throwing as an expression in certain places:
Prior to C# 7.0, if you wanted to throw an exception from an expression body you would have to:
In C# 7.0 the above is now simplified to:
Task<T> is a class and causes the unnecessary overhead of its allocation when the result is immediately available.
ValueTask<T> is a structure and has been introduced to prevent the allocation of a
Task object in case the result of the async operation is already available at the time of awaiting.
ValueTask<T> provides two benefits:
1. Performance increase
- Requires heap allocation
- Takes 120ns with JIT
Here's the analog
- No heap allocation if the result is known synchronously (which it is not in this case because of the
Task.Delay, but often is in many real-world
- Takes 65ns with JIT
2. Increased implementation flexibility
Implementations of an async interface wishing to be synchronous would otherwise be forced to use either
Task.FromResult (resulting in the performance penalty discussed above). Thus there's some pressure against synchronous implementations.
ValueTask<T>, implementations are more free to choose between being synchronous or asynchronous without impacting callers.
For example, here's an interface with an asynchronous method:
...and here's how that method might be called:
ValueTask, the above code will work with either synchronous or asynchronous implementations:
ValueTask struct was being planned to be added to C# 7.0, it has been kept as another library for the time being.
System.Threading.Tasks.Extensions package can be downloaded from Nuget Gallery