C# 8.0 - New Features


In this post, we will see new features introduced in C# 8.0. I will try to explain each feature with simple examples. Below is the list of features that will be discussed:
  1. Default interface method
  2. Nullable ref types 
  3. Ranges and indices 
  4. Null coalescing assignment
  5. Disposable ref struct
  6. Static local functions
  7. Using declaration

Default interface method

Earlier, one major difference between the abstract class and interface was that we could not add a default method in interface once it was implemented in child classes. Now in C# 8.0, we can add the default method to the interface without breaking existing implementation.
  1. interface IDefaultMethodInterface  
  2. {  
  3.    void MustImpMethod();  
  4.    public void DefaultMethod()  
  5.    {  
  6.       Console.WriteLine("This is from DefaultMethod");  
  7.    }  
  8. }  
In the above example, MustImpMethod() doesn't have implementation. However, DefaultMethod() is a concrete method. So the child class must implement MustImpMethod() method, but may or may not implement DefaultMethod(). Below is one such example,
  1. class AnyClass: IDefaultMethodInterface  
  2. {  
  3.    public void MustImpMethod()  
  4.    {  
  5.       Console.WriteLine("This is from AnyClass");  
  6.    }  
  7. }  
If you call these methods through the interface, it will execute successfully.
  1. IDefaultMethodInterface f1 = new AnyClass();  
  2. f1.MustImpMethod();  
  3. f1.DefaultMethod();  
And result will be,
C# 8 New Exciting Features

nullable ref types

The nullable reference type gives you the ability to decide if they can be null or not null. In the following example, you can see notNullablePerson is a reference type, but if you assign a null value to it, it gives a warning message.
To make this reference type nullable, you need to suffix ? to reference the type in declaration, as shown below,
  1. #nullable enable  
  2. Person notNullablePerson = new Person();  
  3. notNullablePerson = null// Warning: Converting null literal or possible null value to non-nullable type.  
  4. Person? nullablePerson = new Person();  
  5. nullablePerson = null//no warning  
  6. #nullable restore  

Ranges and Indices

C# 8.0 introduces two new operators, ^ (System.Index) and ..(System.Range). Let's see how they work with some examples. 
  1. var fruits = new string[]  
  2. {                //Index     // ^ Operator  
  3.    "Apple",         //0         //^5  
  4.    "Orange",        //1         //^4  
  5.    "Banana",        //2         //^3  
  6.    "Mango",         //3         //^2  
  7.    "Custard Apple"  //4         //^1  
  8. };  
  10. Console.WriteLine(fruits[0]); // Result: "Apple"  
  11. Console.WriteLine(fruits[^1]); // Result: "Custard Apple"  
In the above example, you can see that when ^ operator is used items are accessed in the reverse order.
So if you have to access the last item in the sequence, you will use ^1, and for the second to last item ^2, and so on...
Note that the range starts from 1. If you use ^0, it will give you the length of the sequence. So if you try fruits[^0], it will throw an exception. 
Now, let's see how range works,
  1. var fruitRange = fruits[1..3]; // Returns "Orange" and "Banana"  
The starting index in the range is inclusive, but the end index is exclusive.
Below are some examples showing how you can use range to get items in the sequence.
  1. var allFruits = fruits[..]; // Returns all the fruits in array  
  2. var first2Fruits = fruits[..2]; // Returns first 2 fruits  
  3. var last2Fruits = fruits[3..]; // Returns last 2 fruits  
Similar functionality can be achieved by calling System.Index and System.Range factory methods instead of ^ and .. operators. However, it will require lot of code to write.

null coalescing assignment

C# 8.0 introduces the null coalescing assignment operator ??= which ensures that the variable is assigned value only if it is null. Let' see in the below example. 
  1. List<int> numbersList = null;  
  2. //Check if numberList is null  
  3. if(numbersList == null)  
  4. {  
  5.    //then assign value  
  6.    numbersList = new List<int>();  
  7. }  
  8. //Above can be achieved with ??=  
  9. numbersList ??= new List<int>();  
Note, it will not throw any exception if the variable is not null. It will skip the assignment.

Disposable ref struct 

In C# 7.2, structs with ref keyword were introduced so that they can be used in a high-performance scenario. But these ref structs are not allowed to implement any interface. So they can't implement IDisposable interface as well. But in C# 8.0 we can make them disposable by simply defining the Dispose method.
  1. ref struct Person  
  2. {  
  3.    //..  
  4.    public void Dispose()  
  5.    {  
  6.    //clean up unmanaged code here  
  7.    }  
  8. }  

Static local functions

If you want your local function should not use variables from enclosing scope (outer function), then use a static modifier to a local function. If you try to use a variable from the enclosing scope, it will give you an error.
  1. void SendMessage()  
  2. {  
  3.    string name = "John";  
  4.    Console.WriteLine();  
  6.    static string GetMessage()  
  7.    {  
  8.       return $"Hello {name}";  
  9.    }  
  10. }  
We are using the 'name' variable inside a static function. Thus, we are getting an error, as shown below:
error CS8421: A static local function cannot contain a reference to 'name'. 

Using declaration 

With the using declaration, the objects are disposed automatically. In C# 8.0 there's no need to use curly braces to define the scope of the object. See the following example,
  1. using var reader = new StreamReader(@"C:\test\data.txt");  
  2. var content = reader.ReadToEnd();  
  3. Console.WriteLine($"File length: {content.Length}");  
Is equivalent to,
  1. using (var reader = new StreamReader(@"C:\test\data.txt"))  
  2. {  
  3.    var content = reader.ReadToEnd();  
  4.    Console.WriteLine($"File length: {content.Length}");  
  5. }  
With the new syntax (without curly braces), the object is disposed when the closing brace for the method is reached. Whereas with older using statement (with braces), the object is disposed when closing brace of using statement is reached.  
I tried to explain the above features in a simple way. I hope this will help you all understand and use them in future. Thanks for reading article!

Similar Articles