Monday, August 24, 2020

What's Coming in C# 9.0

 In past five years, Microsoft had made a rapid enhancements in C# by introducing a plenty major features with every new version, As planed, C# 9.0 will be officially released with .NET 5 on November 2020. So we will dive into C# 9.0 new features that released on 20 may 2020 as a Preview.

1- Top-level programs

Presently, in C# coding a simple program requires a noticeable amount as initial code as following :

using System;
class Program
{
    static void Main()
    {
        Console.WriteLine("Hello World!");
    }
}

C# 9.0 introduces 

Top-level programs
 feature that, allows you to code your main program (Any statement is allowed) at the top level instead immediately after 
using
 statements and no need to declare any namespace, class or main method.As per C#, you must have only one entry point for your program so, you can exclusively do this in only one file as following:


using System;

Console.WriteLine("Hello World!");

2- Target-typed
new
expressions


new
 expressions always required a type to be specified (except for implicitly typed array or anonymous types) as following.


// Instantiation of point object
Point point = new Point(3, 5);

// Instantiation of dictionary object
Dictionary<string, List<int>> field = new Dictionary<string, List<int>>() {
    { "item1", new List<int>() { 1, 2, 3 } }
};

// implicitly typed array
var items = new[] { 10, 20, 30 };

// Instantiation of anonymous types
var example = new { Greeting = "Hello", Name = "World" };

In C# 9.0 the type is optional if there’s a clear target type that the expressions is being assigned to as following:

// initialization without duplicating the type.
Point point = new(3, 5);
Dictionary<string, List<int>> field = new() {
    { "item1", new() { 1, 2, 3 } }
};

// the type can be inferred from usage.
XmlReader.Create(reader, new() { IgnoreWhitespace = true });

3- Pattern Matching Improvements

C# 7 introduced basic pattern matching features then, C# 8 extended it with new expressions and patterns. By the time, C# 9.0 introduced new pattern enhancements as following:

declaring default identifier for type matching not required

// Before
vehicle switch
{
    Car { Passengers: 0}  => 2.00m + 0.50m,
    Car { Passengers: 1 } => 2.0m,
    Car { Passengers: 2}  => 2.0m - 0.50m,
    Car _                 => 2.00m - 1.0m,
};

// C# 9.0
vehicle switch
{
    Car { Passengers: 0}  => 2.00m + 0.50m,
    Car { Passengers: 1 } => 2.0m,
    Car { Passengers: 2}  => 2.0m - 0.50m,
    // no identifier for default type matching
    Car                   => 2.00m - 1.0m,
};

Relational patterns

C# 9.0 introduces supporting the relational operators 

<
<=
>
, and 
>=
 patterns as following:
public static LifeStage LifeStageAtAge(int age) => age switch
{
   < 0 =>  LiftStage.Prenatal,
   < 2 =>  LifeStage.Infant,
   < 4 =>  LifeStage.Toddler,
   < 6 =>  LifeStage.EarlyChild,
   < 12 => LifeStage.MiddleChild,
   < 20 => LifeStage.Adolescent,
   < 40 => LifeStage.EarlyAdult,
   < 65 => LifeStage.MiddleAdult,
      _ => LifeStage.LateAdult,
};

Logical patterns

C# 9.0 introduces supporting the logical operators 

and
or
, and 
not
 patterns as following:
public static LifeStage LifeStageAtAge(int age) => age switch
{
   <2            =>  LiftStage.Infant,
   >= 2 and <12  =>  LifeStage.Child,
   >= 12 and <18 => LifeStage.Teenager,
   >= 18         => LifeStage.Adult,
};

bool IsValidPercentage(object x) => x is
    >= 0  and <= 100  or    // integer tests
    >= 0F and <= 100F or  // float tests
    >= 0D and <= 100D;    // double tests

Also 

not
 is going to be convenient in if-conditions containing is-expressions where, instead of unwieldy double parentheses.
// before
if (!(e is null)) { ... }

// C# 9.0
if (e is not null) { ... }

4- Covariant return types

By introducing 

covariant return types
 feature in C# 9.0, you are permitted to override a method or a read-only property to return a more derived return type than the method or property overridden as following:
class Compilation ...
{
    virtual Compilation CreateWithOptions(Options options)...
}

class CSharpCompilation : Compilation
{
    override CSharpCompilation CreateWithOptions(Options options)...
}

5- Extending Partial Methods

C# has limited support for developers splitting methods into declarations and definitions/implementations.

Partial methods have several restrictions:

  1. Must have a 
    void
     return type.
  2. Cannot have 
    ref
     or 
    out
     parameters.
  3. Cannot have any accessibility (implicitly 
    private
    ).

One behavior of 

partial
 methods is that when the definition is absent then the language will simply erase any calls to the 
partial
 method .
partial class D
{
    partial void M(string message);

    void Example()
    {
        M(GetIt()); // Call to M and GetIt erased at compile time
    }
    string GetIt() => "Hello World";
}

C# 9.0 extends 

partial
 methods to remove most of the existing restrictions around 
partial
 methods as following:
  1. allow them have 
    ref
     or 
    out
    .
  2. allow 
    non-void
     return types
  3. allow any type of accessibility (
    private
    public
    , etc ..).

Such partial declarations would then have the added requirement that a definition must exist. That means the language does not have to consider the impact of erasing the call sites.

When a 

partial
 method has an explicit accessibility modifier though the language will require that the declaration has a matching definition even when the accessibility is 
private
 as following:
partial class C
{
    // Okay because no definition is required here
    partial void M1();

    // Okay because M2 has a definition
    private partial void M2();

    // Error: partial method M3 must have a definition
    private partial void M3();
}

partial class C
{
    private partial void M2() { }
}

Further the language will remove all restrictions on what can appear on a partial method which has an explicit accessibility. Such declarations can contain 

non-void
 return types, 
ref
 or 
out
 parameters, 
extern
 modifier, etc... These signatures will have the full expressivity of the C# language.
partial class D
{
    // Okay
    internal partial bool TryParse(string s, out int i); 
}

partial class D
{
    internal partial bool TryParse(string s, out int i) { ... }
}

6- Init-only properties

In C#, it is not possible to initialize immutable properties of a class by 

Object initialize
 as following.
// immutable properties 
public class Person
{
    public string FirstName { get; }
    public string LastName { get;}
}

// can't be initialized using Object initialize 
// var person = new Person
// {
//    FirstName = "Ahmed",
//   LastName = "Yousif"
// }

on the other, hand if we want to initialize them using 

Object initialize 
we have to make these properties mutable that, open the door for manipulating it as following:
// mutable properties 
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
...
var person = new Person
{
    FirstName = "Ahmed",
    LastName = "Yousif"
}

// property can be changed
person.FirstName = "Mohamed"

By introducing 

Init-only properties 
C# 9.0 solve that hard equation!

C# 9.0 introduces an 

init
 accessor that allow initializing property only during object initialization as following:
public class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

...
var person = new Person
{
    FirstName = "Ahmed",
    LastName = "Yousif"
}

// manipulating property after initialization won't be allowed
// person.LastName = "Mohamed"

7- Records

Init-only properties
 is a great feature for making individual properties immutable.But what if we want to make the whole object immutable, Here we can highlight the importance of introducing 
Record
 feature. by using 
data
 keyword before 
class 
keyword as following:
public data class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

So, in this case we immuted the whole object which, consider as declaring a record state. records can be shorthand the declaration as following:

// exactly the same declaration before
public data class Person { string FirstName; string LastName; }

If you want to add a private field,you must add 

private
 modifier explicitly:
private string firstName;

8- With-expressions

Now, if we want to change the state of immutable object how we can do it! in this case we have to copy the exist object with updated values to represent a new state. by introducing 

With-expressions
 feature we can achieve it so easy as following:
var person = new Person
{
    FirstName = "Ahmed",
    LastName = "Yousif"
}

// copy person object with update LastName
var anotherPerson = person with { LastName = "Ali" }; 

With-expressions 
feature uses object initializer syntax to set state what’s different in the new object.

9- Value-based equality

Value-based equality
 feature allows you to compare two instances of 
Record
 Class value base equality same as 
Structs
 by comparing all properties in the object using 
Object.Equals(object, object) 
static method. on the other hand if you want check reference base equality you can use 
Object.ReferenceEquals(object, object)
 static method as following:
var person = new Person
{
    FirstName = "Ahmed",
    LastName = "Yousif"
}

// copy person object and with same lastname
var anotherPerson = person with { LastName = "Yousif" }; 

// they have the same values so will be true
// Equals(person, anotherPerson)

// they aren’t the same object so will be false
// ReferenceEquals(person, anotherPerson)
 

10- Positional records

Let's say we want to apply positional approach to a record class to get benefits of positional deconstruction so, in this case you have to explicitly implement constructor and deconstructor of the record as following:

public data class Person 
{ 
    string FirstName; 
    string LastName; 
    public Person(string firstName, string lastName) 
      => (FirstName, LastName) = (firstName, lastName);
    public void Deconstruct(out string firstName, out string lastName) 
      => (firstName, lastName) = (FirstName, LastName);
}

But there's a good news by introducing C# 9.0 there is a shorthand expressing exactly the same thing.

// equivalent to previous one 
public data class Person(string FirstName, string LastName);

now you can deconstructing your object as following:

var person = new Person("Ahmed", "Yousif"); // positional construction
var (fName, lName) = person;              // positional deconstruction

And much more features…

So, the best place to check out the last updates of upcoming features for C# 9.0 is the Language Feature Status on the C# compiler Github repo.

Exciting new features in C# 9

 

Last week at Microsoft Build, there have been a lot of exciting annoucements! .NET 5, Blazor WebAssembly, .NET MAUI, WinUI… But the thing I’m most eager to get my hands on is C# 9, which introduces many interesting new features, so let’s take a quick tour! There’s a long list, so I won’t cover all of them here, but I will highlight the ones I find the most interesting.

Note: Unfortunately the new C# features aren’t supported yet in the latest SDK preview, so we can’t test them in actual projects. Some features can be tried in SharpLab, but things are moving fast, so the bits available in SharpLab don’t always reflect what has been announced at Build.

Update 2020/06/17: A few of the features mentioned in this post are now available in the .NET 5 preview 5 SDK.

Target typed new

In C# 9, it will be possible to omit the type in object creation expressions, if the type can be inferred from the context, making code terser and less repetitive:

private Dictionary<string, object> _properties = new();

Parameter null-checking

This feature introduces a simple syntax to automate null checks on method parameters. For instance, this code :

public string SayHello(string name)
{
    if (name == null)
        throw new ArgumentNullException(nameof(name));
    return $"Hello {name}";
}

Can be simplified to this:

public string SayHello(string name!) => $"Hello {name}";

The ! after the parameter name automatically inserts a null check for that parameter.

Pattern matching improvements

C# 9 comes with a few improvements to pattern matching. The most useful, in my opinion, is the not pattern, which lets you write code like this:

if (foo is not null) { ... }
if (animal is not Dog) { ... }

Relational (<>=, etc.) and logical operators (and and or) can also be used in pattern matching:

return size switch
{
    < 10 => "small",
    >= 10 and < 100 => "medium",
    _ => "large"
}

Records and with expressions

This is the big one, in my opinion. Creating simple data types in C# have always been more painful than it should be; you have to create a class, declare properties, add a constructor if you want your type to be immutable, override Equals and GetHashCode, maybe add a deconstructor, etc. C# 7 tuples made this a little easier, but still not ideal since a tuple is anonymous. The new Record feature in C# 9 makes things much easier!

For instance, a simple class representing a point might look like this, if you implement equality, deconstructor, etc.

public class Point : IEquatable<Point>
{
    public Point(int x, int y) =>
        (X, Y) = (x, y);

    public int X { get; }

    public int Y { get; }

    public bool Equals(Point p) =>
        (p.X, p.Y) == (X, Y)

    public override bool Equals(object other) =>
        other is Point p && Equals(p);

    public override int GetHashCode() =>
        (X, Y).GetHashCode();

    public void Deconstruct(out int x, out int y) =>
        (x, y) = (X, Y);
}

In C# 9, using the Records feature, the above class can be reduced to this:

public data class Point(int X, int Y);

Yup, just one line, and not even a long one! How great is that? Note that it also works with structs, if you need a value type.

Note that records are immutable: you can’t change the values of their properties. So if you want to modify an instance of a record type, you need to create a new one (this should be familiar, since the same is true of dates and strings, for instance). The current approach would be to do something like this:

Point p1 = new Point(1, 2);
Point p2 = new Point(p1.X, 3);

Basically, copy all properties from the original instance, except the ones you want to change. In this case, it’s OK because there are only 2 properties, but it can quickly become annoying when there are many properties.

C# 9 introduces with expressions, which let you do this instead:

Point p1 = new Point(1, 2);
Point p2 = p1 with { Y = 3 };

with expression makes a clone of the original object, with the modified properties specified between curly brackets.

There are several sub-features related to records (e.g. init-only properties), that I won’t cover here. Check out Mads Torgersen’s article for a more in-depth description.

Target-typed conditionals

There’s a small thing that has been annoying C# developers for years: when using the conditional operator (also known as “ternary”), there must be a type conversion from one side to the other. For instance, this code doesn’t compile:

Stream s = inMemory ? new MemoryStream() : new FileStream(path);

Because there’s no conversion between MemoryStream and FileStream. To fix it, one side has to be explicitly cast to Stream.

In C# 9, the code above will be allowed, if both sides are convertible to the target type (in this case, Stream).

Covariant return

Currently, when you override a method from a base class, the overriding method must return the same type as the base class method. In some situations, it would be more practical to return a more specific type. C# 9 makes this possible by allowing overriding methods to return a type that derives from the base method’s return type:

public class Thing
{
    public virtual Thing Clone() => new Thing();
}

public class MoreSpecificThing : Thing
{
    // Override with a more specific return type
    public override MoreSpecificThing Clone() => new MoreSpecificThing();
}

Top-level statements

This feature aims to reduce boilerplate code for simple programs. Currently, even the simplest program needs a class with a Main method:

using System;

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello world");
    }
}

This just adds noise, and make things confusing for beginners. C# 9 will make it possible to omit the Program class and Main method, so that the code above can be simplified to this:

using System;
Console.WriteLine("Hello world");

Conclusion

Most of the features introduced by C# 9 are relatively small ones, designed to make code simpler, less cluttered and more readable; they’re very convenient, but probably won’t change how we write code in a very fundamental way. Records are another story, though; they make it much easier and less painful to write immutable types, which I hope will encourage developers to take advantage of immutability whenever possible.

Note that the release of C# 9 is still a few months away, and things are still moving, so some of the features I mentioned in this post could be modified, postponed to a later version, or even abandoned completely.

Cache Design and patterns

 In this article  we will look at application design and how cache design will be helping to get data from back end quickly.  scope of this ...