C# 8 : Nullable Reference Types, Null Forgiving and Null Coalescing Assignment

There are some properties that ought to be learned together to understand the complete picture. In this blog post, we will address two of such features.

Nullable Reference Types

Let us begin by writing some code.

public class Foo
{
    public Bar Bar { get; set; }
}

public class Bar
{
    public int Id { get; set; }
}

// Client Code
var foo = new Foo();
Console.WriteLine(foo.Bar.Id);

Notice anything wrong ? How often have been guilty of attempting to access something that could potentially be null ? In the above case, we haven’t initialized the Foo.Bar property, but in the client code, we are attempting to access the Bar.Id property.

This would throw a NullReferenceException, as you are dereferencing a null value (Bar hasn’t been assigned yet). We should have checked for null in the first place.

But as the code base increases, there are times when we forget to do so. And unfortunately, prior to C# 8, there was no way compiler could warn us to do the null check even if we might have wondered many a times if it could do so.

Nullable Reference Types in C# 8 brings us pretty much the same. It allows us to mark the Reference Types which can/cannot be null, using the same syntax we used to mark Nullable Value Types – using the ? operator.

In C# 8, with Nullable Reference Types enable, if you were to compile the code, the compiler warns you with the following.

Warning	CS8618	Non-nullable property 'Bar' is uninitialized. Consider declaring the property as nullable.

Depending on your business logic, you have couple of ways to proceed from here.

Case 1 – Bar is Nullable

If your business logic requires that the Bar property could be null, then you could mark it explicitly. For example,

public Bar? Bar { get; set; }

The ? operator tells the compiler that the property could be null and it should warn the developer for possible null checks if he hasn’t done so yet. The compiler duely warns you with the following.

Warning	CS8602	Dereference of a possibly null reference.

Case 2 – Bar is non-nullable

If your application logic requires that the Bar property cannot be null, then you could let the compiler know by using the following syntax (basically omitting the ? operator).

public Bar Bar { get; set; }

The above code tells the compiler that the Bar cannot be null. Interestingly, we still get a warning now, let us first examine what it is before understanding why compiler throws the warning.

Warning	CS8618	Non-nullable property 'Bar' is uninitialized. Consider declaring the property as nullable.

We have declared the property as Non-nullable, yet we have left it uninitialized. We could of course remove this warning by initializing the property with a default value (either in the property declaration or in the constructor), but this gives us an oppurtunity to introduce another feature.

Null Forgiving Operator

You could have a situation where, as per the business logic, you know that a particular property can never be null at runtime. However, you are in no position to assign a default value. In such situations, you could use the Null-forgivingoperator to disable the warnings. Null-Forgiving is denoted by using the postfix operator !. For example,

public Bar Bar { get; set; } = null!;

The above code informs the compiler that it would skip the warning about Bar being uninitialized and it would not be null in runtime. Do note the null-forgiving operator has not runtime influence.

Null Coalescing Assignment

C# 8 also introduces the Null Coalescing assignment via the the assignment operator ??=. For demonstration, let us further develop our code and introduce a CreateBarInstance to assign instances of Bar. For example,

public void CreateBarInstance(Bar instance)
{
    Bar = instance;
}

Due to some business reasons, we have requirement that the method should assign Bar instance, only if it is null. Of course, one could do this an if condition, but that would introduce a lot of boiler plate code. We could eliminate these extraneous code using the null coalescing operator.

public void CreateBarInstance(Bar instance)
{
    Bar ??= instance;
}

The line Bar ??= instance ensures that Bar is assigned only when the it is null. Or in other words, the value of right hand operand is assigned to left hand operand only if the left hand operand is null.

Demonstrating the code in action,

var foo = new Foo();
foo.CreateBarInstance(new Bar(2));
Console.WriteLine(foo.Bar.Id);
foo.CreateBarInstance(new Bar(3));
Console.WriteLine(foo.Bar.Id);

Output of the above code

2
2

That’s all for now, we will continue our exploration of C# 8 features.

C# 8 : Asynchronous Streams and Static Local Functions

Asynchronous Stream
How often have you come across situations when you would want to return streams of data from an asynchronous method ? Plenty of times I would guess. The asynchronous methods were pretty limited when return data in streams, but not any longer. 

Consider the following code.

// Will not compile
public static async Task<IEnumerable<int>> Generate(int max)
{
    for (int i = 0; i < max; i++)
    {
        await Task.Delay(1000);
        yield return i;
    }
}

The above code would not compile. There is no way you could `yield return` values (or in other words, stream values) from an asynchronous method. This would have been a highly useful situation when you want to iterate over results from a query over an extremely large database. There are other countless situation the ability to stream data from asynchronous method could be useful. However, prior to C# 8.0, we were severely handicapped in such a situation.

With C# 8.0, .Net comprises a new Type called the IAsyncEnumerable, which allows us to accomplis this. The above code could be now rewritten as.

public static async IAsyncEnumerable<int> Generate(int max)
{
    for (int i = 0; i < max; i++)
    {
        await Task.Delay(1000);
        yield return i;
    }
}

//client code
await foreach (var item in Generate(10))
{
    Console.WriteLine(item);
};

The placement of `await` keyword in the consumer code needs to be noted here.

Local Static Functions
In previous versions of C#, local methods could capture the enclosing scope. For example, consider the following code.

public void Foo()
{
    int internalValue = 20;

    void Bar()
    {
        internalValue++;
    }

    Bar();
}

This enable usage of variables such as internalValue within the local method. If the usage is accidental, this might lead to huge consequences. One could not declare a static local method in the earlier versions of .Net.

With C# 8.0, .Net removed this limitation. This enables the developers to create *pure local functions* as it does not allow usage of variables from enclosing types within it.

public void Foo()
{
    int internalValue = 20;

    static void Bar()
    {
        Console.WriteLine("Do Something");
        // internalValue++; // This would throw compile error
    }

    Bar();

}

This also removes certain overhead created when using the captured variables. For example, the previous code (C# < 8.0), would be treated by the compiler as

public void Foo()
{
	DemoClass.<>c__DisplayClass0_0 CS$<>8__locals1;
	CS$<>8__locals1.internalValue = 20;
	DemoClass.<Foo>g__Bar|0_0(ref CS$<>8__locals1);
}
internal static void <Foo>g__Bar|0_0(ref DemoClass.<>c__DisplayClass0_0 A_0)
{
	Console.WriteLine("Do Something");
	int internalValue = A_0.internalValue;
	A_0.internalValue = internalValue + 1;
}

Notice how the compiler generates a struct to store the captured variables and pass it using `ref`. Such an overhead would be completely avoided when using the *static local functions*, simply because – the compiler won’t compile or capture the enclosing scope.

We explored two features introduced in C# 8.0. We will explore more in coming posts, possibly in bunches as we have plenty to catch up before exploring C# 9.0.

C# 8 : Using Declaration

The next feature we would explore in C# 8 is more off a syntatic sugar, but neverthless it is important to understand the difference between the new syntax and the original feature it is covering up. We are all aware of the Using statements, which allows correct usage of the IDisposible objects.

Using Statement

Let us begin by writing an example with the Old way of doing things. We will first introduce our Custom Object with IDisposible implemented.

public interface ITalk
{
void Talk(string message);
}

public class CustomDisposibleObject : IDisposable,ITalk
{
public void Dispose()
{
Console.WriteLine($"Disposing {nameof(CustomDisposibleObject)}");
}

public void Talk(string message)
{
Console.WriteLine($"{nameof(CustomDisposibleObject)}-{nameof(ITalk.Talk)} : {message}");
}
}

We will now use CustomDisposibleObject with the Using Statement.

public int UsingStatement()
{
using (var customDisposibleObject = new CustomDisposibleObject())
{
customDisposibleObject.Talk(nameof(IExample.UsingStatement));
return default;
}
}

The Using statement is of course a syntatic sugar over the Try-Finally block. The compiler translates the above code as (check using Telerik’s JustDecompile)

public int UsingStatement()
{
int num;
CustomDisposibleObject customDisposibleObject = new CustomDisposibleObject();
try
{
customDisposibleObject.Talk("UsingStatement");
num = 0;
}
finally
{
if (customDisposibleObject != null)
{
customDisposibleObject.Dispose();
}
}
return num;
}

So far, so good. So what is the real problem with the Using Statement and why would one introduce the Using Declaration. To begin with, the Using Statement syntax is way too verbose and breaks the normal flow of syntax. This is where the Using Declaration comes into picture.

Using Declaration

The Using Declaration Sytax removes much of the ceremony associated with the Using Statement blocks. For example, if one were rewrite the above code via the new Using Declaration.

public int UsingDeclaration()
{
using var customDisposibleObject = new CustomDisposibleObject();
customDisposibleObject.Talk(nameof(IExample.UsingDeclaration));
return default;
}

The cerimonial braces are now gone and the code look less verbose. The obvious question would be – When is the object disposed ?

The object is disposed when it leaves the scope, which in the case of the code above is the method.

So how does the new syntax gets translated by the compiler ? Let us rerun the JustDecompile and check it.

public int UsingDeclaration()
{
int num;
CustomDisposibleObject customDisposibleObject = new CustomDisposibleObject();
try
{
customDisposibleObject.Talk("UsingDeclaration");
num = 0;
}
finally
{
if (customDisposibleObject != null)
{
customDisposibleObject.Dispose();
}
}
return num;
}

As you can observe, there is no difference at all. The new syntax removes the cerimonial braces, making the code look less nested.

Sample Code for this article could be found in my Github.

C# 8 : Index and Range

While C# as a language has grown leaps and bounds, one are that was least addressed was manipulation of collections (arrays in particular). C# 8 looks to change exactly that by introducing two new Types.

System.Index

System.Index is a structure that can be used to Index a collection either from the start or the end. In previous versions of the language, there was no direct way to index a collection from the end. For example,

// Get the 2nd element from end in the array
var secondElementFromLast = arr[arr.Length-2];

This changes with introduction of the System.Index, which fortunately is introduced with its own syntatic sugar. The code above, for accessing the second last element could be now rewritten as

// Get the 2nd element from end in the array
var secondElementFromLast = arr[^2]; // With the uniary prefix 'hat' operator

To access the elements from the begining of the array, you could use

// Get 2nd element from the start in the array
var secondElementFromStart = arr[2]; // No changes here

System.Range

In previous versions of C#, there was no easy way to get a slice of the collection. Let’s say, you wanted to get all elements from 2nd to 5th element in the array. You could achieve this using Linq using the Enumerable.Skip and Enumerable.Take methods. For example

var slice = list.Skip(1).Take(4);

C# 8.0 introduces the System.Range type, again with support of syntatic sugar to ease the life of developers. The above code could now be rewritten as

var slice = list[1..5];

The Range Structure represents range that has a start and end indexes and is represented by binary infix x..y. Do note that both operands of the range could be ommited to provide different meanings. For example

var slice1 = list[5..]; // All elements starting from the 5th element in collection
var slice2 = list[..5]; // First 5 elements in collection
var slice3 = list[..^5]; // Elements starting from first till the 5th element from last
var slice4 = list[^5..]; // Last 5 elements in collection
var slice5 = list[..]; // Entire List

We will continue exploring newer features of the language in coming blog posts.

C# 8.0 : Default Implementation of Interface Members

The way C# language is evolving is definitely exciting and one feature that truly makes the wait for C# 8.0 all the more exciting is default implementation of interfaces.

Since the onset, interfaces were behaviors characterized by member signatures, strictly disallowing any implementation. All that is going to change when C# 8.0 rolls out, as it supposedly, plans to support default implementation of interfaces. What it means is further trimming down the walls between interface and class (but remember, walls would still exist : Interface and Classes will still have significant difference, we will talk about it a bit later.)

Let’s look at an example, courtesy MSDN Blogs.

interface ILogger
{
void Log(LogLevel level, string message);
void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload
}

class ConsoleLogger : ILogger
{
public void Log(LogLevel level, string message) { ... }
// Log(Exception) gets default implementation
}

Doesn’t that look exciting ? Gone are days when as an Author of an interface, you were almost handicapped extending the interface fearing it might break any of implementing code. You could now provide the default implementation of your interface member, and still be assured that all implementing code works as fine as it was before.

If this doesn’t excite you, think from another perspective. What this implies is possibility of multiple inheritance (almost) in a language that was never really supported it. You could now provide two interfaces with default implementation, and have a class inherit from both.

interface IParentA
{
string MethodA() => nameof(MethodA);
}

interface IParentB
{
string MethodB() => nameof(MethodB);
}

class Child : IParentA,IParentB
{
void MethodChild()
{
Console.WriteLine($"{nameof(MethodA)} = {MethodA()}");
Console.WriteLine($"{nameof(MethodB)} = {MethodB()}");
}
}

This effectively brings in Multiple Inheritance in place. What would be interesting to see is how Microsoft circumvented the Diamond Problem. Let’s delve a bit into Diamond Problem first, before trying to understand Microsoft’s workaround from what ever we know as of now.

Diamond Problem

Let’s consider the following inheritance structure assuming C# were to support Multiple Inheritance.

class A
{
public virtual string DemoMethod() => $"{nameof(A)}.{nameof(A.DemoMethod)}()";
}

class B : A
{
public override string DemoMethod() => $"{nameof(B)}.{nameof(B.DemoMethod)}()";
}

class C : A
{
public override string DemoMethod() => $"{nameof(C)}.{nameof(C.DemoMethod)}()";
}

// Let's assume this works for sake of demonstration
class D:B,C
{
public void Print()=> Console.WriteLine(DemoMethod());
}

In the above code, class D inherits from both Class B and Class C, both of which, in-turns inherits from Class A and overrides virtual member DemoMethod. Assuming this code compiles, what would be the output of Print() Method in Class D. Will it invoke method from Class B or Class C ? This ambiguity is better known as Diamond Problem.

Microsoft intents to work around the Diamond Problem here by taking the most specific override at the time. As Microsoft pens it down

“Diamonds with Classes

A class implementation of an interface member should always win over a default implementation in an interface, even if it is inherited from a base class. Default implementations are always a fallback only for when the class does not have any implementation of that member at all.”

Let’s rewrite the above code using default implementation of interfaces.

interface A
{
string DemoMethod() => $"{nameof(A)}.{nameof(A.DemoMethod)}()";
}

interface B: A
{
override string DemoMethod() => $"{nameof(B)}.{nameof(B.DemoMethod)}()";
}

interface C: A
{
override string DemoMethod() => $"{nameof(C)}.{nameof(C.DemoMethod)}()";
}

// This would be Error as no most specific override for A.DemoMethod()
class D:B,C
{
public void Print()=> Console.WriteLine(DemoMethod());
}

// This would be Error as no most specific override for A.DemoMethod()
class E:B,C,A
{
string DemoMethod()=> B.base.DemoMethod();
public void Print()=> Console.WriteLine(DemoMethod());
}

So will now abstract class == interface ?

No, not really. Even though the differences are diminishing, there still lies a lot of difference between the two. To name a few
– Unlike Abstract Class ,you still can’t write a default constructor for interface.
– Covariance and Contravariance

Essentially, abstract class and interface would continue to coexist in its own space. What Microsoft has done with Default Implementation of Interface is making use of Traits (more on traits later.)