What's new in C# 8.0?

What's new in C# 8.0?

Introduction

C# as a type-safe, object-oriented and component-oriented programming language is at the heart of the .Net framework. This framework includes a wider range of utilities such as windows apps, database apps, web apps, web services, XML apps, etc. that each one has its own pace of development. So the gradual changes in different versions of the language are inevitable. 

In this article, I've introduced some of the main improvements of this language in its 8.0 version.

Improved and new features of the C# 8.0

Readonly modifier:

any types preceding by readonly keyword shows that it does allow to change the value of that type. In C# 8.0 it was the first time we were able to add the "readonly" modifier to the members of a struct! It gives more control on the states of the members.

some points are worth knowing: "readonly" methods have to apply to the "readonly" properties. we must indicate the read-only accessors explicitly to the compilers, they do not infer automatically. But the auto-implemented accessors are exceptions!

Default interface methods:

There are some fixed methods in interfaces that should be implemented by the inherited classes or interfaces. But in the 8.0 version, we can add more customized methods in any interfaces.

More new capabilities in pattern matching feature:

this feature introduced in C# 7.0 version, and we see some developments in the later versions. In the 8.0 version, three more features are using "is" expression, switch statement and switch expression for their implementations:

replacing the switch statements with switch expressions: The ordinary switch statement is illustrated in the code below. We place the switch variable in front of the switch keyword and consider multiple cases inside its scope. Each case assesses the feasibility of case matching for the proper execution, and the default case executes when none of the cases was passed.

No alt text provided for this image

In C# 8.0, the main change of the pattern matching is converting statements to expressions. The other changes are placing the variable behind the switch keyword, replacing the case and ":" with =>, and replacing the default keyword with "_". These changes tend to increase the code readability:

No alt text provided for this image

property pattern matching: In this case, we can check special values of each property or field of an expression. In this example, the date expression has three properties named Year, Month, and Day, and the "is" expression checks whether they are the same as the specified values or not.

No alt text provided for this image

Positional and tuple patterns: this feature helps to deconstruct a combination of some variables and return them discretely. The tuple data structure, as a positional container of multiple types, can be used for this purpose as well:

No alt text provided for this image

Using declarations:

The purpose of the "using" declaration is to limit the scope of a variable. In fact, it invokes the dispose function at the end of the scope. We can use it in two different shapes. In the first form, we add the using keyword at the beginning of the variable definition as code below. In this case, the variable specified by the "using" declaration will be expired by the end of the enclosing statement execution.

static int WriteLinesToFile(IEnumerable<string> lines)
{
    using var file = new System.IO.StreamWriter("WriteLines2.txt");
    int skippedLines = 0;
    foreach (string line in lines)
    {
        // some implementation ...
    }
    // Notice how skippedLines is in scope here.
    return skippedLines;
    // file is disposed here
}

In the second form, we can define a separate block for the "using" declaration. In this case, the variable specified by the using keyword will be expired at the end of the using scope.

static int WriteLinesToFile(IEnumerable<string> lines)
{
    using (var file = new System.IO.StreamWriter("WriteLines2.txt"))
    {
        int skippedLines = 0;
        foreach (string line in lines)
        {
            // some implementation ...
        }
        return skippedLines;
    } // file is disposed here
}

Static local functions:

we use a static modifier that shows a static member. In C# 8.0, we can add this declaration keyword to the local functions. This capability prevents access to the local variables or instances. For example, the static Add function in the code below doesn't change and have access to any local variables in the Method().

int Method()
{
    int y = 5;
    int x = 7;
    return Add(x, y);

    static int Add(int left, int right) => left + right; // passes the compilation

   
}

Ref structures and IDisposable:

structures can inherit from any interfaces but if they are defined as "ref", they can not inherit from any interfaces including IDisposabel! So the direct implementation of the void Dispose() method could be the solution. It is also applicable to the "readonly ref" structs.

Nullable reference types:

Nullable reference variables are usually instantiated by the null value. For defining a variable as a nullable reference type, the "?" character must be added to the end of its type name. However, the compiler does not check the null instantiation of the nullable reference types, but if they are assigned to non-nullable types, they must be checked against the null value.

Asynchronous streams:

Data streams are consists of multiple stream items which are usually retrieved or generated asynchronously. Asynchronous streams are introduced in C# 8.0. It has three features: The async keyword must be added as a modifier of the method signature, the return type is of type IAsyncEnumerable<T>, and a yield return statement.

The Async streams are based on IAsyncEnumerable<T> interface which provides an asynchronous enumerator over the collection of items of a specified type, and T is the type of items in the collection.

Asynchronous Dispose:

for doing asynchronous resource clean up, we can implement the IAsyncDisposable.DisposeAsync() method of the System.IAsyncDisposable interface which is introduced in C# 8.0. The DisposeAsync() returns a ValueTask type as an asynchronous dispose operation.

The succinct syntax for working with indices and ranges:

In C# 8.0, there are introduced two new signs making it easier to work with sequences. The System.Index represents the indices of a sequence. We usually use the number of indices to point to a specific element in a sequence, in this version for any "n" as an index, we can use "n^" to access the "sequence.Length-n" element of the collection. The System.Range provides a sequential order of elements. For extracting any subset of a collection, we can use ".." as a range operator. In any range, the start of the interval is inclusive but the end is exclusive. So the range [0..^0] represents the whole sequence and it is equivalent of [0..sequence. Length].

In addition to arrays, the string, Span<T>, and ReadOnlySpan<T> types can use the range and indices operators.

Null-coalescing assignment:

Previously we've talked about the null reference types. Here we introduce "??" as the non-coalescing operator, which returns the left-hand operand if its value is not null. And "??=" as the non-coalescing assignment operator to assign the right-hand value to a reference type (the left-hand operand) if its value is null.

No alt text provided for this image

Unmanaged constructed types:

In the C# programing language, the unmanaged types are any of the following types: sbyte, byte, short, short, int, unit, long, long, char, float, double, decimal, or bool types, andy enum types, any pointer types, any user-defined struct types. The unmanaged types do not have any references managed by the garbage collectors.

In C# 7.3 the constructed types are considered as unmanaged types, but starting with C# 8.0, if the constructed types contain the unmanaged types only, they could be considered as unmanaged types as well.

Enhancement of interpolated verbatim strings:

one of the main characteristics of @ as a verbatim identifier is to indicate that a string literal will be interpreted as a verbatim string, and $ is an interpolated string character. Their combination shapes an interpolated verbatim string character. In the previous versions of the C# programming language, we were able to use the "$@" combination of these characters but in the C# 8.0, we can use the "@$" order of them as well.

This code shows the effect of a verbatim string literal:

No alt text provided for this image

Stackalloc in nested expressions:

Stackalloc is a stack that is allocated on a block of memory. It is more recommended to assign the stackalloc expressions to the Span<T> or ReadOnlySpan<T> types. In C# 8.0, we can use the stackalloc expressions inside the nested expressions where the Span<t> or ReadOnlySpan<T> are allowed.

What distinguished the stackalloc expressions, is that they are not disposed of by the garbage collector or any explicit invocation. But they have created from their declaration statement in the method and will be discarded till the finishing point of that method.


Note: All the materials used in this article are adapted from multiple Microsoft documents and the StackOverflow website around this topic.

要查看或添加评论,请登录

Elahe Dorani的更多文章

  • What's new in C# 7.0 through 7.3?

    What's new in C# 7.0 through 7.3?

    Introduction In C# version 7.0 which is released within Visual Studio 2017, we are facing some newer and cooler…

  • What is new in C# 6.0?

    What is new in C# 6.0?

    In the 6.0 version of the C# programming language, the main focus of language designers were on adding some features to…

    1 条评论
  • Chapter 18: Asynchronous Language Features

    Chapter 18: Asynchronous Language Features

    Introduction The CPU of digital gadgets are the brain of devices. The operating systems on them would consider an…

    1 条评论
  • Chapter 10: LINQ

    Chapter 10: LINQ

    Introduction LINQ, the Language Integrated Query, is an integrated set of technologies using to apply some queries on…

    3 条评论
  • Chapter 9: Delegates, Lambdas, and Events

    Chapter 9: Delegates, Lambdas, and Events

    Introduction Delegates are some types that provide a reference to the methods. Events are some notification occurred in…

  • Chapter 8: Exceptions

    Chapter 8: Exceptions

    Introduction In the execution process of any programs, there could occur some errors and it is on the CLR decide to run…

  • Garbage Collection

    Garbage Collection

    The garbage collector is an automatic mechanism to detect and collect the dead object’s memory space and make it…

  • Chapter 7: Object Lifetime

    Chapter 7: Object Lifetime

    Introduction C# language programming supports the object-oriented paradigm. It means that almost all operations in this…

  • Chapter 6: Inheritance

    Chapter 6: Inheritance

    C# as an object-oriented language has some essential features which make it possible to produce object-oriented…

  • chapter 5: Collections

    chapter 5: Collections

    Introduction The data is the main concept we want to deal with in every program we write. The most important thing we…

社区洞察

其他会员也浏览了