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:
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.
- interface IDefaultMethodInterface
- {
- void MustImpMethod();
- public void DefaultMethod()
- {
- Console.WriteLine("This is from DefaultMethod");
- }
- }
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,
- class AnyClass: IDefaultMethodInterface
- {
- public void MustImpMethod()
- {
- Console.WriteLine("This is from AnyClass");
- }
- }
If you call these methods through the interface, it will execute successfully.
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,
- #nullable enable
- Person notNullablePerson = new Person();
- notNullablePerson = null;
- Person? nullablePerson = new Person();
- nullablePerson = null;
- #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.
- var fruits = new string[]
- {
- "Apple",
- "Orange",
- "Banana",
- "Mango",
- "Custard Apple"
- };
-
- Console.WriteLine(fruits[0]);
- Console.WriteLine(fruits[^1]);
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,
- var fruitRange = fruits[1..3];
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.
- var allFruits = fruits[..];
- var first2Fruits = fruits[..2];
- var last2Fruits = fruits[3..];
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.
- List<int> numbersList = null;
-
- if(numbersList == null)
- {
-
- numbersList = new List<int>();
- }
-
- numbersList ??= new List<int>();
Note, it will not throw any exception if the variable is not null. It will skip the assignment.
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.
- ref struct Person
- {
-
- public void Dispose()
- {
-
- }
- }
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.
- void SendMessage()
- {
- string name = "John";
- Console.WriteLine();
-
- static string GetMessage()
- {
- return $"Hello {name}";
- }
- }
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,
- using var reader = new StreamReader(@"C:\test\data.txt");
- var content = reader.ReadToEnd();
- Console.WriteLine($"File length: {content.Length}");
Is equivalent to,
- using (var reader = new StreamReader(@"C:\test\data.txt"))
- {
- var content = reader.ReadToEnd();
- Console.WriteLine($"File length: {content.Length}");
- }
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!