C# 8 New Features

C# 8 New Features with Examples

In this article, I am going to discuss C# 8 New Features with Examples. Here, I will explain the features with simple examples. From our next article, I will explain them in detail with real-time use cases. It is also worth mentioning that the main feature concertation in C# 7 was to add safe, efficient code to C#, and in C# 8, we have seen more big language features and the preparation for the Records, which are planned to be released with C# 9. At the end of this article, you will have a very good understanding of C# 8, and hopefully, you will be better prepared to use C# 8, and for the new C# challenges, you will meet in the future.

C# 8 New Features

C# 8.0 adds the following features and enhancements to the C# language:

  1. Readonly Struct Members
  2. Default Interface Methods
  3. Pattern Matching Enhancements
  4. Using Declarations
  5. Static local functions
  6. Disposable ref structs
  7. Nullable reference types
  8. Asynchronous streams
  9. Asynchronous disposable
  10. Indices and ranges
  11. Null-coalescing assignment
  12. Unmanaged constructed types
  13. Stackalloc in nested expressions
  14. Enhancement of interpolated verbatim strings

Note: In order to work with C# 8 features, we are going to use Visual Studio 2019 and Creating .NET Core 3.1 Console Applications.

Readonly Struct in C# 8

In readonly structure, we declare the structure with the readonly modifier and readonly structure indicates that the given structure is immutable. When you create a readonly structure, it is necessary to use a readonly modifier with its fields, if you do not do this, then the compiler will give an error. Even, if you need to use only get accessors, if you use set accessors, then you will get an error.

For a better understanding, please have a look at the below example. In the below example, we declare the structure as readonly as well as the Height and Width fields as readonly with the get accessors only.

using System;
namespace Csharp8Features
{
    public readonly struct Rectangle
    {
        public readonly double Height { get; }
        public readonly double Width { get; }
        public double Area => (Height * Width);
        public Rectangle(double height, double width)
        {
            Height = height;
            Width = width;
        }
        public override string ToString()
        {
            return $"(Total area for height: {Height}, width: {Width}) is {Area}";
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Rectangle rectangle = new Rectangle(10, 20);
            Console.WriteLine("Height: " + rectangle.Height);
            Console.WriteLine("width: " + rectangle.Width);
            Console.WriteLine("Rectangle Area: " + rectangle.Area);
            Console.WriteLine("Rectangle: " + rectangle);
            Console.ReadKey();
        }
    }
}
Output:

C# 8 New Features with Examples

For a detailed understanding of Readonly Struct in C#, please click on the below URL.

https://dotnettutorials.net/lesson/readonly-structs-in-csharp-8/

Default Interface Methods in C# 8:

Before C# 8.0 interfaces only contain the declaration of the members (methods, properties, events, and indexers), but from C# 8.0 it is allowed to add members as well as their implementation to the interface. Now you are allowed to add a method with their implementation to the interface without breaking the existing implementation of the interface, such type of method is known as default interface methods (also known as the virtual extension methods).

The main benefit of the Default method is that it allows us to add new functionality to the interfaces of our libraries and ensure the backward compatibility with code written for older versions of those interfaces. For a better understanding, please have a look at the below example.

using System;
namespace Csharp8Features
{
    interface IDefaultInterfaceMethod
    {
        public void DefaultMethod()
        {
            Console.WriteLine("I am a default method in the interface!");
        }
    }
    class AnyClass : IDefaultInterfaceMethod
    {
    }
    class Program
    {
        static void Main(string[] args)
        {
            IDefaultInterfaceMethod anyClass = new AnyClass();
            anyClass.DefaultMethod();
            Console.ReadKey();
        }
    }
}
Output:

C# 8 New Features with Examples

For a detailed understanding of Default Interface Methods in C#, please click on the below URL.

https://dotnettutorials.net/lesson/default-interface-methods-csharp-8/

Enhanced Pattern Matching in C# 8

C# 7.0 first introduced syntax for type patterns and constant patterns by using the is the 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. Recursive patterns are patterns that can contain other patterns. In C# 8, the .Net Development team has introduced the following Patterns.

  1. Switch Expressions
  2. Property Patterns
  3. Tuple Patterns
  4. Positional Patterns

For a detailed understanding of Enhanced Pattern Matching in C# 8, please click on the below URL.

https://dotnettutorials.net/lesson/pattern-matching-in-csharp-8/

Using Declarations in C# 8

With the new C# 8 “using declarations”, the code with the using statement can be simplified. Now, the curly brackets are no longer required. At the end of the scope of the method (which is here the end of the main method), the Dispose method is also called automatically. Here also, the compiler creates a *try/finally block to make sure Dispose method is called. For a better understanding, please have a look at the below example.

using System;
namespace Csharp8Features
{
    public class UsingDeclarations
    {
        public static void Main()
        {
            using var resource = new Resource();
            resource.ResourceUsing();
            Console.WriteLine("Doing Some Other Task...");
        }
    }

    class Resource : IDisposable
    {
        public Resource()
        {
            Console.WriteLine("Resource Created...");
        }
        public void ResourceUsing()
        {
            Console.WriteLine("Resource Using...");
        }
        public void Dispose()
        {
            Console.WriteLine("Resource Disposed...");
        }
    }
}
Output:

C# 8 New Features with Examples

For a detailed understanding of Using Declarations in C# 8, please click on the below URL.

https://dotnettutorials.net/lesson/using-declarations-csharp-8/

Static Local Functions in C# 8

Local functions are introduced in C# 7. But in C# 7, it is not possible to use a static modifier with the local function i.e. static local functions are not allowed. This feature is added in C# 8.0. from C# 8.0, we are allowed to use a static modifier with the local function. This ensures that the static local function does not reference any variable from the enclosing or surrounding scope. If a static local function tries to access the variable from the enclosed scope, then the compiler will throw an error. Let us understand this with an example.

using System;
namespace Csharp8Features
{
    public class LocalFunctions
    {
        public static void Main()
        {
            Calculate();
        }
        public static void Calculate()
        {
            int X = 20, Y = 30;
            CalculateSum(X, Y);

            static void CalculateSum(int Num1, int Num2)
            {
                int sum = Num1 + Num2;
                Console.WriteLine($"Num1 = {Num1}, Num2 = {Num2}, Result = {sum}");
            }

            CalculateSum(30, 10);
            CalculateSum(80, 60);
        }
    }
}
Output:

C# 8 New Features with Examples

For a detailed understanding of Static Local Functions in C# 8, please click on the below URL.

https://dotnettutorials.net/lesson/static-local-functions-in-csharp-8/

Disposable Ref Structs in C# 8

From C# 7.2 onward, a struct can be declared with the ref keyword. This allows the instances of ref structs to be allocated on the stack and restricts them from moving onto the managed heap. However, this also enforces some limitations over the ref structs, the ref structs cannot implement any interface.

In C# 8.0, a special exception to the above limitation was made for the IDisposable interface. Ref structs can now be disposable without implementing the IDisposable interface, simply by having a Dispose method in them. Let us understand this with an example.

using System;
namespace Csharp8Features
{
    public class DisposableRefStructs
    {
        public static void Main()
        {
            using var book = new Rectangle(10, 20);
            Console.WriteLine($"Area of Rectangle : {book.GetArea()}");
            Console.WriteLine("Main Method End");
        }
    }

    ref struct Rectangle
    {
        private double Height { get; set; }
        private double Width { get; set; }
        public Rectangle(double height, double width)
        {
            Height = height;
            Width = width;
        }

        public double GetArea()
        {
            return Height * Width;
        }

        public void Dispose()
        {
            Console.WriteLine("Rectangle Object Disposed Of");
        }
    }
}

For a detailed understanding of Disposable Ref Structs in C# 8, please click on the below URL.

https://dotnettutorials.net/lesson/disposable-ref-structs-in-csharp-8/

Nullable Reference Types in C# 8

C# 8.0 allows us to specify whether a variable should be null, and when it cannot be null. Based on these annotations, the compiler will warn you when you are potentially using a null reference or passing a null reference to a function that will not accept it.

In C#, a reference type refers to an object that is created on the heap memory. When the reference type variable does not point to any object, then its value is null. Prior to C# 8.0, all reference types were nullable. Nullable reference types refer to a group of features introduced in C# 8.0 that you can use to minimize the likelihood that your code causes the runtime to throw NullReferenceException. Let us understand this with an example.

using System;
namespace Csharp8Features
{
    public class NullableReferenceTypes
    {
        public static void Main()
        {
            string message = null;

            // warning: dereference null.
            Console.WriteLine($"The length of the message is {message.Length}");

            var originalMessage = message;
            message = "Hello, World!";

            // No warning. Analysis determined "message" is not null.
            Console.WriteLine($"The length of the message is {message.Length}");

            // warning!
            Console.WriteLine(originalMessage.Length);
        }
    }
}

For a detailed understanding of Nullable Reference Types in C# 8, please click on the below URL.

https://dotnettutorials.net/lesson/nullable-reference-types-in-csharp-8/

Asynchronous Streams in C# 8

Async Streams is the new feature in C# 8.0 which provides async support for handling streams or IEnumerable data. In this article, we cover all the aspects of Async Streams (IAsyncEnumerable) including how to use ConfigureAwait and how to use CancellationToken as well. Cancellation token can be a great way to manage async programming in Dotnet core and C# but with Async Streams, it can be a bit difficult and at times if not used properly can give errors. We shall be discussing briefly everything about AsyncStreams. IAsyncEnumerable also has options and support for CancellationToken as well as ConfigureAwait. Let us understand this with an example.

using System;
using System.Threading.Tasks;

namespace Csharp8Features
{
    public class NullableReferenceTypes
    {
        static async Task Main(string[] args)
        {
            await foreach (var number in GenerateSequence())
            {
                Console.WriteLine(number);
            }
        }
        public static async System.Collections.Generic.IAsyncEnumerable<int> GenerateSequence()
        {
            for (int i = 0; i < 20; i++)
            {
                await Task.Delay(100);
                yield return i;
            }
        }
    }
}

For a detailed understanding of Asynchronous Streams in C# 8, please click on the below URL.

https://dotnettutorials.net/lesson/asynchronous-streams-in-csharp-8/

Asynchronous Disposable in C# 8

Starting with C# 8.0, the language supports asynchronous disposable types that implement the IAsyncDisposable interface. You use the await using statement to work with an asynchronously disposable object. Let us understand this C# 8 new Feature with an example.

using System;
using System.Threading.Tasks;
using System.IO;
namespace Csharp8Features
{
    class AsynchronousDisposable
    {
        static async Task Main(string[] args)
        {
            await using (var disposableObject = new Sample())
            {
                Console.WriteLine("Welcome to C#.NET");
            } // DisposeAsync method called implicitly

            Console.WriteLine("Main Method End");
        }
    }

    public class Sample : IAsyncDisposable
    {
        static readonly string filePath = @"D:\MyTextFile1.txt";
        private TextWriter? textWriter = File.CreateText(filePath);

        public async ValueTask DisposeAsync()
        {
            if (textWriter != null)
            {
                textWriter = null;
            }

            await Task.Delay(1000);
            Console.WriteLine("DisposeAsync Clean-up the Memory!");
        }
    }
}

For a detailed understanding of Asynchronous Disposable in C# 8, please click on the below URL.

https://dotnettutorials.net/lesson/asynchronous-disposable-in-csharp-8/

Indices and Ranges in C# 8

As we already know about the Range and Indices. We use them several times in our programs, they provide a short syntax to represent or access a single or a range of elements from the given sequence or collections. In this article, we will learn what’s newly added in the range and indices in C# 8.0. The Range and Indexes make the C# syntax simpler and more readable.

Ranges and Indices in C# allow more natural syntax for accessing single items or ranges in a sequence. This language support relies on two new types and two new operators. Let us understand this C# 8 new Features with an example.

using System;
using System.Threading.Tasks;
namespace Csharp8Features
{
    class IndicesAndRanges
    {
        static async Task Main(string[] args)
        {
            var countries = new string[]
            {
                "INDIA",
                "USA",
                "UK",
                "NZ",
                "CANADA",
                "CHINA",
                "NEPAL",
                "RUSIA",
                "SRILANKA",
                "INDONESIA"
            };

            Index i1 = 4;
            Console.WriteLine($"{countries[i1]}"); // Output: "CANADA"

            // Index 4 from end of the collection
            Index i2 = ^4;
            Console.WriteLine($"{countries[i2]}"); // Output: "NEPAL"
        }
    }
}

For a detailed understanding of Indices and Ranges in C# 8, please click on the below URL.

https://dotnettutorials.net/lesson/indices-and-ranges-in-csharp-8/

Null-Coalescing Assignment Operator in C# 8

C# 8.0 introduces the null-coalescing assignment operator ??=. We can use this ??= operator in C# to assign the value of its right-hand operand to its left-hand operand only if the left-hand operand evaluates to null. That means the null-coalescing assignment operator ??= assigns a variable only if it is null. Let us understand this C# 8 new Features with an example.

using System;
using System.Threading.Tasks;
namespace Csharp8Features
{
    class NullCoalescingAssignment
    {
        static async Task Main(string[] args)
        { 
            int? Age = null;
            Age ??= 20;
            Console.WriteLine(Age);
        }
    }
}

For a detailed understanding of Null-Coalescing Assignment Operator in C# 8, please click on the below URL.

https://dotnettutorials.net/lesson/null-coalescing-assignment-operator-in-csharp-8/

Unmanaged Constructed Types in C# 8

A type is called constructed type if it is generic and the type parameter is already defined, such as List<string>, List, etc. In C# 7.3 and earlier, a constructed type (a type that includes at least one type of 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.

Beginning with C# 7.3, you can use the unmanaged constraint to specify that a type parameter is a non-pointer, non-nullable unmanaged type. Beginning with C# 8.0, a constructed struct type that contains fields of unmanaged types only is also unmanaged. Let us understand this C# 8 new Feature with an example.

using System;
namespace Csharp8Features
{
    public struct Coords<T>
    {
        public T X;
        public T Y;
    }

    public class UnmanagedTypes
    {
        public static void Main()
        {
            DisplaySize<Coords<int>>();
            DisplaySize<Coords<double>>();
        }

        private unsafe static void DisplaySize<T>() where T : unmanaged
        {
            Console.WriteLine($"{typeof(T)} is unmanaged and its size is {sizeof(T)} bytes");
        }
    }
}

For a detailed understanding of Unmanaged Constructed Types in C# 8, please click on the below URL.

https://dotnettutorials.net/lesson/unmanaged-constructed-types-in-csharp-8/

Stackalloc in Nested Expressions in C# 8

The stackalloc operator in C# allocates a block of memory in the stack. The memory block will be created during the execution of the method, and it is automatically deleted when the method is returned. You cannot explicitly free the memory allocated with stackalloc. A stack-allocated memory block is not subject to garbage collection and doesn’t have to be pinned with a fixed statement. Let us understand this with an example.

using System;
namespace Csharp8Features
{
    public class StackMemoryAllocation

    {
        public static void Main()
        {
            Span<int> set = stackalloc[] { 1, 2, 3, 4, 5, 6 };
            var subSet = set.Slice(3, 2);
            foreach (var n in subSet)
            {
                Console.WriteLine(n); // Output: 4 5
            }
        }
    }
}

For a detailed understanding of Stackalloc in Nested Expressions in C# 8, please click on the below URL.

https://dotnettutorials.net/lesson/stackalloc-in-nested-expressions-in-csharp-8/

In the next article, I am going to discuss Readonly Structs in C# 8 with Examples. Here, in this article, I try to explain C# 8 New Features with Examples. I hope you enjoy this C# 8 New Features with Examples article.

Leave a Reply

Your email address will not be published.