You can apply the
readonly modifier to members of a struct. It indicates that the member doesn’t modify state. It’s more granular than applying the
readonly modifier to a
Default interface methods
You can now add members to interfaces and provide an implementation for those members. This language feature enables API authors to add methods to an interface in later versions without breaking source or binary compatibility with existing implementations of that interface. Existing implementations inherit the default implementation. This feature also enables C# to interoperate with APIs that target Android or Swift, which support similar features. Default interface methods also enable scenarios similar to a “traits” language feature.
More patterns in more places
Pattern matching gives tools to provide shape-dependent functionality across related but different kinds of data. C# 7.0 introduced syntax for type patterns and constant patterns by using the
is expression and the
switch statement. These features represented the first tentative steps toward supporting programming paradigms where data and functionality live apart. As the industry moves toward more microservices and other cloud-based architectures, other language tools are needed.
C# 8.0 expands this vocabulary so you can use more pattern expressions in more places in your code. Consider these features when your data and functionality are separate. Consider pattern matching when your algorithms depend on a fact other than the runtime type of an object. These techniques provide another way to express designs.
In addition to new patterns in new places, C# 8.0 adds recursive patterns. The result of any pattern expression is an expression. A recursive pattern is simply a pattern expression applied to the output of another pattern expression.
A using declaration is a variable declaration preceded by the
using keyword. It tells the compiler that the variable being declared should be disposed at the end of the enclosing scope.
Static local functions
You can now add the
static modifier to local functions to ensure that local function doesn’t capture (reference) any variables from the enclosing scope. Doing so generates
CS8421, “A static local function can’t contain a reference to <variable>.”
Disposable ref structs
struct declared with the
ref modifier may not implement any interfaces and so can’t implement IDisposable. Therefore, to enable a
ref struct to be disposed, it must have an accessible
void Dispose() method. This feature also applies to
readonly ref struct declarations.
Nullable reference types
Inside a nullable annotation context, any variable of a reference type is considered to be a nonnullable reference type. If you want to indicate that a variable may be null, you must append the type name with the
? to declare the variable as a nullable reference type.
For nonnullable reference types, the compiler uses flow analysis to ensure that local variables are initialized to a non-null value when declared. Fields must be initialized during construction. The compiler generates a warning if the variable isn’t set by a call to any of the available constructors or by an initializer. Furthermore, nonnullable reference types can’t be assigned a value that could be null.
Nullable reference types aren’t checked to ensure they aren’t assigned or initialized to null. However, the compiler uses flow analysis to ensure that any variable of a nullable reference type is checked against null before it’s accessed or assigned to a nonnullable reference type.
Starting with C# 8.0, you can create and consume streams asynchronously. A method that returns an asynchronous stream has three properties:
- It’s declared with the
- It returns an IAsyncEnumerable<T>.
- The method contains
yield returnstatements to return successive elements in the asynchronous stream.
Consuming an asynchronous stream requires you to add the
await keyword before the
foreach keyword when you enumerate the elements of the stream. Adding the
await keyword requires the method that enumerates the asynchronous stream to be declared with the
async modifier and to return a type allowed for an
async method. Typically that means returning a Task or Task<TResult>. It can also be a ValueTask or ValueTask<TResult>. A method can both consume and produce an asynchronous stream, which means it would return an IAsyncEnumerable<T>.
Indices and ranges
Indices and ranges provide a succinct syntax for accessing single elements or ranges in a sequence.
This language support relies on two new types, and two new operators:
- System.Index represents an index into a sequence.
- The index from end operator
^, which specifies that an index is relative to the end of the sequence.
- System.Range represents a sub range of a sequence.
- The range operator
.., which specifies the start and end of a range as its operands.
Let’s start with the rules for indexes. Consider an array
0 index is the same as
^0 index is the same as
sequence[sequence.Length]. Note that
sequence[^0] does throw an exception, just as
sequence[sequence.Length] does. For any number
n, the index
^n is the same as
sequence.Length - n.
A range specifies the start and end of a range. The start of the range is inclusive, but the end of the range is exclusive, meaning the start is included in the range but the end isn’t included in the range. The range
[0..^0] represents the entire range, just as
[0..sequence.Length] represents the entire range.
C# 8.0 introduces the null-coalescing assignment operator
??=. You can use the
??= operator to assign the value of its right-hand operand to its left-hand operand only if the left-hand operand evaluates to
Unmanaged constructed types
In C# 7.3 and earlier, a constructed type (a type that includes at least one type argument) can’t be an unmanaged type. Starting with C# 8.0, a constructed value type is unmanaged if it contains fields of unmanaged types only.
Stackalloc in nested expressions
Starting with C# 8.0, if the result of a stackalloc expression is of the System.Span<T> or System.ReadOnlySpan<T> type, you can use the
stackalloc expression in other expressions.
Enhancement of interpolated verbatim strings
Order of the
@ tokens in interpolated verbatim strings can be any: both
@$"..." are valid interpolated verbatim strings. In earlier C# versions, the
$ token must appear before the
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> </Project> The <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> <PropertyGroup> <LangVersion>8.0</LangVersion> </PropertyGroup> </Project>
- 7, 6, 5, 4, and 3