DEV Community

Cover image for ๐Ÿ”„ C# 9.0 Features and Expectations of C# 10
ByteHide
ByteHide

Posted on • Updated on • Originally published at bytehide.com

๐Ÿ”„ C# 9.0 Features and Expectations of C# 10

The latest version of C#, 9.0, was officially released withย .NET 5 in November 2020. These days there are already rumors of the features of the future version, C# 10. โœ…

One of the biggest advantages of open source software is being able to see how the project evolves over time as the days go by. With this we want to refer to the same C#, since we can follow its progress on GitHub and see its main news.

Let's start with the most important features of C# 9.0 ๐Ÿค—

๐Ÿ”ผ Module Initializers

In this latest version of C # 9.0, the [ModuleInitializer] attribute is used to specify a method that we can invoke before any code in the module, the destination method must be static, without any type of parameter and returned empty.


using system;
using System.Runtime.CompilerServices;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine($"Data={Data}");
    }
    public static string Data;
    [ModuleInitializer]
    public static void Init()
    {
        Data="This static method is invoked before any other method in the module";
    }
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”ผ Extension GetEnumerator

The foreach statement normally operates on a variable of type IEnumerator when it contains a definition of any public extension for GetEnumerator.

This is how we can see it in this example ๐Ÿ‘‡

using system;
using System.Collections.Generic;

    IEnumerator<string> colors = new List<string> {"blue", "red", "green"}.GetEnumerator();
foreach (var colors in colors)
{
    Console.WriteLine($"{color} is my favorite color");
}
public static class Extensions
{
    public static IEnumerator<T> GetEnumerator<T>(this IEnumerator<T> enumerator) => enumerator;
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”ผ Covariant Returnย Types

In C# 9.0, the return types of override methods are usually much more specific than the declarations in the base type ๐Ÿ‘‡


abstract class Weather
{
    public abstract Temperature GetTemperature();
}

class Spain : Weather
{
    public override Celsius GetTemperature() => new Celsius();
}
class USA : Weather
{
    public override Farenheit GetTemperature() => ne Farenheit();
}

class Temperature{ }
class Celsius{ }
class Farenheit{ }
Enter fullscreen mode Exit fullscreen mode

The GetTemperature () method has the return type Temperature, the derived class Spain overrides this method and returns a specific type Celsius.

It is a feature that makes our code more flexible. โœ…

๐Ÿ”ผ Initย Accessor

The init accessor makes immutable objects easier to create and use ๐Ÿ‘‡


Point point1 = new() { X = 1, Y = 2};
Console.WriteLine(point1.ToString());

public record Point
{
    public int X { get; init;}
    public int Y { get; init;}
}
Enter fullscreen mode Exit fullscreen mode

The init accessor can be used with Structures, Registers and Classes. The init accessor can be used with Classes, Structures, and Registers ๐Ÿ‘‡


Point point1 = new() { X = 1, Y = 2};
Point point2 = point1 with { Y = 4};
Console.WriteLine(point1.ToString());

public record Point
{
    public int X { get; init;}
    public int Y { get; init;}
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”ผ Records

Now we have a new type of reference called record that gives us equal value. To better understand it, we have this example ๐Ÿ‘‡


Point point1 = new(1, 2);
Console.WriteLine(point1.ToString());
Point point2 = new(1, 2)};
Console.WriteLine(point1.Equals(point2));

public record Point
{
    public int X { get;}
    public int Y { get;}
    public Point(int x, int y) => (X, Y) = (x, y);
}
Enter fullscreen mode Exit fullscreen mode

As we can see, the point record is immutable, you can greatly simplify the syntax using init accesor, since its properties are read-only.

๐Ÿ”ผ Lambda Discard Parameters

The next C# 9.0 improvement is being able to use discard (_) as an input parameter of a lambda expression in case that parameter is not used.


//C#8
button.Click += (s, e) => {Message.Box.Show("Button clicked"); };

//C#9
button.Click += (_, _) => {Message.Box.Show("Button clicked"); };
Enter fullscreen mode Exit fullscreen mode

It is a feature that also allows us to read the code in a cleaner and more beautiful way.

๐Ÿ”ผ Target-Typed new

Another very important feature in this latest version of C# is the ability to omit the type of a new expression when the object type is explicitly known.

Let's see a quick and simple example ๐Ÿ‘‡


Point point = new() {X = 1, Y = 2};
Console.WriteLine($"point:({point1.X}, {point.Y})");

public class Point
{
    public int X { get; set; }
    public int Y { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

It is a very useful feature since it allows you to read the code in a clean way without having to duplicate the type.


Point point = new(1, 2);
Console.WriteLine($"point:({point1.X}, {point.Y})");

public class Point{

    public int X { get; }

    public int Y { get; }

    public Point(int x, int y) => (X, Y) = (x, y);
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”ผ Top-Level Statements

In C# 9.0, it is possible to write a top-level program after using declarations.

Here we can see the example ๐Ÿ‘‡


using System;

Console.WriteLine("Hello World!");
Enter fullscreen mode Exit fullscreen mode

With top-level declarations, you wouldn't need to declare any space between names, main method, or class program. This new feature can be very useful for programmers just starting out, as the compiler does all of these things for you.


[CompilerGenerated]
internal static class $program
{
    private static void $main(string[] args)
    {
        Console.WriteLine("Hello World!");
    }
}
Enter fullscreen mode Exit fullscreen mode

Seeing the new features in C# 9.0, which help make programming much simpler and more intuitive.

What can we expect in the future version?

Okay, let's talk about the future version ๐Ÿ‘‡

  • What could bring new? ๐Ÿค”
  • What would you like me to have? ๐Ÿค”
  • What is the possibility of it being added?๐Ÿค”

Note that the upcoming features are still debatable and are not certain to appear in C# 10.

Keep in mind that they are not simply ideas or contributions from the community. These features that I am going to mention are being shuffled by its developers. And although they are not implemented in the next version, today they are being refined so that they come out in future versions of C#.

Let's start ๐Ÿ‘

๐Ÿ”ผ File-level namespaces

All of us when we started programming in C# we have created a "Hello World" application. Knowing this we also know that C# uses a block structure for namespaces.


namespace HelloWorld
 {
 class Hello
 { 
     static void Main(string[] args)
     {
         System.Console.WriteLine("Hello World!");
     }
 }
 }
Enter fullscreen mode Exit fullscreen mode

The best thing about this is that namespaces can be overlaid very easily, simply by nesting blocks. In the same way that a single file can contain types in any combination of namespaces and multiple files can share the same namespace between them.

If we want to scratch the negative part a bit, this system adds a bit of indentation if we compare it with bracket languages such as JavaScript or Java.

The question we ask ourselves at this point is:

Is it possible to keep that functionality, but at the same time reduce excess indentation? ๐Ÿค”

Yes โœ…

How is it possible? ๐Ÿค”

It simply opened that entering namespaces with file scope, this would allow to establish a default namespace that would be applied automatically to the entire file eliminating the indentation.


namespace HelloWorld; public class Hello
{
    static void Main (string[] args)
    {
        System.Console.WriteLine("Hello World!");
    }
}
Enter fullscreen mode Exit fullscreen mode

It is normal to only have one file scoped namespace per file, so there would be no problem. Likewise, most C# code files do not include more than one namespace.

If for example we add a namespace block to a file that uses a file-scoped namespace, a nested namespace is simply created.

Let's see a quick example ๐Ÿ‘‡


namespace Company.Product;
Company.Product.Component

namespace Component
{
}
Enter fullscreen mode Exit fullscreen mode

It is clear that it is not a very big feature, but it is preferable that the more improvements there are, the easier and more intuitive the task of programming will be.

๐Ÿ”ผ Primary constructors

In the latest released versions of C#, the topic of boilerplate code has been reduced considerably with features like automatic properties.

The main improvement of this is not simply reducing the amount of code that is written, but reducing the amount of code that has to be read. It makes navigating code bases easier and reduces the most common places where errors can occur.

Primary constructors are a very good implementation that would again reduce the amount of code that is written. We can see it with this simple example that has a class that has a constructor and two read-only properties.


public class DataSlice
 {
   public string DataLabel { get; }
   public float DataValue { get; }

   public DataSlice(string dataLabel, float dataValue)
   {
      DataLabel = dataLabel;
      DataValue = dataValue;
   }
 }
Enter fullscreen mode Exit fullscreen mode

What the statistics tell us, is that 70% of its classes have constructors, and more than 90% of all of them simply do nothing more than copy parameters into properties.

If you haven't written any kind of constructor code yet, don't worry as we can still create and use the class in the same way.


var adultData = new DataSlice("Vaccinated adults", 741);
Enter fullscreen mode Exit fullscreen mode

By using the main constructor, property validation is not excluded. In the same way, its rules can be enforced in a property setter.

Let's see an example ๐Ÿ‘‡


public class DataSlice(string dataLabel, float dataValue)
 {
 public string DataLabel
 {
   get => dataLabel;
   set
   {
     if (value < 0) throw new ArgumentOutOfRangeException();
     dataLabel = value;
   }
 }
 public float DataValue { get => dataValue; } 
 }
Enter fullscreen mode Exit fullscreen mode

Other details are also possible (calling the base constructor in a derived class, adding constructors). The main downside to all of this is that the primary constructors could collide with the position registers.

๐Ÿ”ผ Raw stringย literals

We already know that the ordinary strings that C# has, tend to be quite messy since they need quotation marks (''), newlines (\ n) and backslashes (). What C# offers before this little problem is the use of special characters.

For example, we can prefix a string with @ and have free rein to add all these details without any problem ๐Ÿ‘‡

string path = "c:\\path\\backslashes";
string path = @"c:\pathh\backslashes";
Enter fullscreen mode Exit fullscreen mode

What the raw string literal string allows is to create new paths to avoid escaping problems. Using the delimiter of a series of quotes followed by a line break to start, and a line break followed by the same number of quotes to close.

To understand it more simply, I leave you this example below ๐Ÿ‘‡


string xml = """
          <part number="2021">
            <name>year</name>
            <description>this is the actual year
             <ref part="2020">year</ref> actual year.
            </description>
          </part>
          """;
Enter fullscreen mode Exit fullscreen mode

If your concern is that there is a possibility of a triple quote sequence within the string, you can simply extend the delimiter so that you can use all the quotes you want, as long as the beginning and end are respected.


string xml = """" 
             Now """ is safe to use in your raw string.
             """";
Enter fullscreen mode Exit fullscreen mode

In the same way as @ strings, newlines and whitespace are preserved in a raw string. What happens is that the common white space, that is, the amount that is used to bleed, is cut off.

Let's see more simply with an example ๐Ÿ‘‡


<part number="2021">
            <name>year</name>
            <description>this is the actual year
             <ref part="2020">year</ref> actual year.
            </description>
          </part>
Enter fullscreen mode Exit fullscreen mode

To this ๐Ÿ‘‡


<part number="2021">
  <name>year</name>
  <description>this is the actual year
   <ref part="2021">year</ref> actual year.
  </description>
</part>
Enter fullscreen mode Exit fullscreen mode

With this I want to explain to you that the raw strings are not intended to replace the @ strings that you are using right now.

Rather, they are prepared for the specific moments when you need a marked block or arbitrary code and in turn you need a coding approach that is guaranteed to be safe.

๐ŸŸข Conclution:

To finish this article, we think that C# still has many years of travel ahead of it and it still has many things to add to make the task of programming even easier and more optimal.

Top comments (0)