Together with .net core 3.0, Microsoft release C# 8.0. Today, I want to take a look at the new features of C# 8.0 using .net core 3.0.
Nullable Reference Types
Avoiding NullReferenceExceptiony can be hard. Therefore C# 8.0 introduces nullable reference types which help us to avoid null reference mistakes at compile-time. This new feature allows you to mark properties as nullable and non-nullable. For example, you can have nullable and non-nullable strings now. Let’s see some code:
I have created a new class, Car, with two string properties.
I expected the compiler to give me a warning that Brand and Make could be null. But everything seems fine. Nullable Reference Types are an opt-in feature. This means that you have to enable the feature to be able to use it. It should be enabled for new C# 8.0 projects, but it didn’t work for me. To enable it add <TargetFramework>netcoreapp3.0</TargetFramework> to the PropertyGroup of your .csproj file.
After adding this tag, you will see two compiler warnings and visual effects under the properties.
If you want them to be null, you can use the Elvis operator to mark them as nullable. This removes the compiler warning.
Another useful feature is that the compiler warns you of possible null reference exceptions when you use a nullable property. In the following screenshot, I try to access the Length of the Make property. Since I haven’t initialized the Make property, this would lead to a NullReferenceException.
The compiler is great but not perfect. Sometimes a property can’t be null but the compiler still gives you the warning. Then you can tell the compiler that this property is never null by using the ! operator.
In this case, you shouldn’t use it though because the compiler is right and the property is null.
Pattern matching was first introduced in C# 7. C# 8.0 extends the pattern matching by allowing us to use more types of patterns in more places.
Property patterns allow you to check one or multiple properties of an object and return a value, depending on the properties. In my example, I have a house class with a name and floors. If the house has exactly 10 floors and is named Skyscraper, I return true. Otherwise, I return false. This is not the best example but I am not really good at coming up with examples 😉
This feature allows you to switch over objects and return different values, depending on the type of the object and additionally on the properties of the example. For this demo, I created three classes, Circle, Rectangle and Triangle. I pass the object to a method and the method returns information about the object, depending on its type.
On the screenshot above, you can see the switch statement for my three different objects. You can also have a switch in a switch. I use this to return a different text if the object is a normal rectangle or a square. The switch expression doesn’t allow a fall-through, the, therefore no break is needed after the case. The last line handles the default case, using the _ to catch all cases which weren’t handled before.
Nesting switch statements in the switch can be handy but don’t overdo since your code can become hard to read very fast.
Tuple patterns are similar to switch expressions. Here you can pass two values (a tuple) which are evaluated in the case. In my demo, I pass two colors and return the result if you mix these two colors. For example, red and blue will lead to purple.
The last two lines are interesting. The second last line, (_, _) when color1 == color2 doesn’t care which colors are passed, as long as both have the same value. The last one works as the default case and returns unknown when no case was hit before.
Indices and Ranges
Working with indices and ranges can be confusing. I am gonna try to explain them first before I show a code demo. C#8.0 provides the new range operator .. which allows you to work with ranges. For example, you can have an array with ascending numbers from 1 to 10. With the range operator, you could select all items from index 2 to 8. It is important to note that the beginning of a range is inclusive and the end is exclusive. This means the range [1..8] returns the numbers 2 – 8. This is due to the previously mentioned inclusion of the beginning and exclusion of the end. Additionally, C#, like many other languages is zero index-based. This means that index 1 will give you the second number, therefore 2.
You can select an index based on the beginning of the array but also based on the end of the array. To use an offset of the end of the array, use the ^ operator. For example [^1] will give you the last number of the array. Note that [^0] will lead to an index out of range exception because it works the same way as array.Length. This would also give you an out of range exception due to the zero-based indexing.
Let’s code some examples which should make this feature clearer.
Additional C# 8.0 Features
C# 8.0 brings too many new features to highlight here. Additional features are:
- Default Interface Members
- Using Declarations
- Async Streams
- Static Local Functions
- Disposable ref Structs
- Many more
For more information about the new features of C# 8.0 see Microsoft’s documentation.
In this post, I presented some of the new features of C# 8.0 like nullable reference types, pattern matching, and async streams. There are many more new features that you can look into in detail in the official documentation.
You can find the code of today’s demo on GitHub.