facebook

Blog

Stay updated

Let’s see together what the version of C# currently under development could reserve for us
A look to the future: C# 9
Wednesday, April 15, 2020

In my previous article (part one and part two), we saw the news features introduced in version 8 of C #, but the team is already working on version 9, and we can appreciate some changes.

Obviously, at the moment the new features are still under development and until the final release there might be some variations. The most curious ones, however, will appreciate a look at the work that the Microsoft Developer Team is doing.

Simplified Parameter NULL Validation Code

One of the changes under consideration by the team simplifies the control over null values by shortening the syntax using an annotation “!” on a method parameter name:

// Before:
void Insert(string s) 
{
    if (s is null)
        throw new ArgumentNullException(nameof(s));
}
 
// After:
void Insert(string s!) 
{
}

Primary Constructors

The Primary Constructor has the purpose of simplifying the declaration of properties and constructors of a class:

// Before:
class Person
{
    private string _name;
 
    public Person(string name)
    {
        _name = name;
    }
 
    public string Name
    {
        get => _name;
        set {
                if (value == null) 
                {
                   throw new NullArgumentException(nameof(Name)); 
                }
                 _name = value;
            }
    }
}
 
// After:
class Person(string name)
{
    public string Name
    {
        get => name;
        set
        {
            if (value == null)
            {
               throw new NullArgumentException(nameof(Name));
            }
            name = value;
        }
    }
}

Record

The team would like to provide a way to set up a read-only member in the objects’ initializers. therefore, a new initonly modifier has been introduced to be applied to properties and fields.

A Record can contain operators, methods and properties. By default, the latter are read-only (Immutable Type). The Records can be of a value type or a reference type and allow us to compare structural equality. Records are useful for representing complex data with many properties, such as a database record or a DTO. Let’s see an example:

data class UserInfo
{
    public string Username { get; }
    public string Email { get; }
    public bool IsAdmin { get; } = false;
}

When working with immutable types, you generally make changes to an object by building a modified copy of it instead of making the changes directly on the object. Now you could opt instead for something like this:

var userInfo = new UserInfo() 
{
    Username = "Francesco",
    Email = "francesco@microsoft.com",
    IsAdmin = true
};
var newUserName = userInfo with { Username = "Vas" };

The resulting newUserName object would be a copy of userInfo, but with the Username property with the value = “Vas”. As we said, the equality between Records is compared by structure and not by reference:

var userInfo1 = new UserInfo() {
    Username = "Francesco",
    Email = "francesco@microsoft.com",
    IsAdmin = true
};
var userInfo2 = new UserInfo() 
{
    Username = "Francesco",
    Email = "francesco@microsoft.com",
    IsAdmin = true
};
var compareUsersInfo = userInfo1 == userInfo2; // true

Enum class

Linked to the Records seen previously, an enum class could be a new way of writing an abstract class and the concrete classes that implement it. If there are multiple partial definitions of the enum class, all members will be considered members of the definition of the enum class. Unlike a user-defined abstract class definition, the root type of the enum class is partial by default, and it is structured to have only a default private constructor, without parameters. User-defined constructors are not allowed.

Furthermore, no type can inherit directly from it in any declaration, other than those specified as members of the enum class itself.

// If we had this situation:
public partial abstract class Shape { }
 
public class Rectangle : Shape {
 
  public double Width { get; }
  public double Length { get; }
 
  public Rectangle(double Width, double Length){
    this.Width = Width;
    this.Length = Length;
  }
}
 
public class Circle : Shape {
 
  public double Radius { get; }
 
  public Circle(double Radius)
  {
    this.Radius = Radius;
  }
}
 
// It could be:
enum class Shape
{
  Rectangle(double Width, double Length);
  Circle(double Radius);
}

The enum classes can also contain value members, which define public static properties of type get-only on the root type and return the root type itself:

enum class Color
{
    Red, Green
}

which is equivalent to

partial abstract class Color
{
    public static Color Red => ...;
    public static Color Green => ...;
}

Discriminated Unions

Similar to discriminated unionin F# (a functional / Object-Oriented hybrid language of .NET world), this feature is connected to the previous ones and offers a way to define and use data types that can contain any number of different types.

Mixing the features seen above, let’s take a look at the following code:

public class Person  // Define a record type
{  
  public initonly string Firstname { get; }  
  public initonly string Lastname { get; }  
};  
 
enum class IntOrBool { int I; bool B; }  // Define an enum class
 
enum class MixedType // Define an enum class
{
    Person P;
    IntOrBool Union;

Now let’s create the instances of the Record and the Union:

var person = new Person()  
{  
  Firstname = “Francesco”;  
  Lastname = “Vas”;  
};  
   
var unionRecord = new MixedType.P(person); // Record C# 9  
var unionTypeInt = new MixedType.Union(I 86); // Int type
var unionTypeBool = new MixedType.Union(B false); // Boolean type

Therefore, we could have a function that returns one of the three types belonging to the MixedType root type (that is Personintbool) on which we can use the pattern matching.

For demonstration purposes, let’s see some other examples of possible use.

Exception handling:

try { ... }
catch (CommunicationException|SystemException ex) {
    // Handle the exception
}

Pattern matching:

if (result is short|int|long number) { ... }

Type constraint:

public class GenericClass<T> where T : T1 | T2 | T3

Typed heterogeneous collections:

var inputs = new List<int|double|string>{3, 9.9, "abc"};

And, or, and not patterns

The last feature we are going to examine is the possible implementation of three new ways of evaluating a switch case:

switch (obj)
{
    case 1 or 2:
    case Point(0, 0) or null:
    case Point(var x, var y) and var p:
}

Conclusions

Good. I think it’s enough! As we said, everything is still to be defined, and the developers are working hard to simplify our work and introduce new features.

I hope I have aroused your curiosity, and if you are interested, I invite you to visit the GitHub repository which contains all the proposals and discussions under consideration by the Microsoft Team (God save the open-source!):

https://github.com/dotnet/csharplang/milestone/15

Stay tuned!

Written by

Francesco Vastarella