What’s coming to C# 8

|  Posted: September 25, 2018  |  Categories: Community Events TechMeet360

.Net Conf is a free, virtual developer event co-organized by the .NET community and Microsoft. Over the course you have a wide selection of live sessions that feature speakers from the community and .NET product teams. These are the experts in their field and it is a chance to learn, ask question live, and get inspired for your next software project. The sessions are mostly about .NET Core and ASP.NET Core, C#, F#, Roslyn, Visual Studio, Xamarin, and much more.

Mads Torgersen is the Program Manager for the C# Language at Microsoft and runs the C# design meetings. Prior to joining Microsoft in 2005, he was an associate professor at the University of Aarhus, doing programming language research. In .Net Conf he presented a session on What’s coming to C#. So in this blog we are going to see what are all the features that are available in C# 8.

C# 8

Default Interface Members

In current version of C#, you can only able to declare the methods, but you cannot able to define the methods.

Interface ILogger

{  

        void Log(LogLevel level, string message);                                          //allowed    

        void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); //not allowed

}

Anyway to achieve the above functionality, abstract classes can be used.

abstract class Logger

{

       voidLog(LogLevellevel, stringmessage);    

       voidLog(Exceptionex) =>Log(LogLevel.Error, ex.ToString());

}

But in C# 8.0 you can able to able to define the methods in interfaces

Interface ILogger

{

       void Log(LogLevel level, string message);

       void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); //allowed

}

And if a class implements the above interface, it’s not necessary to define all the methods. Anyway if you want to define the methods in the implemented class, by doing this the default definition will be over ridden.

class TempLogger : ILogger

{

       public void Log(LogLevel level, string message) { }

       public void Log(Exception ex) { }

}

So this allows us to extend an existing interface with a default interface method definition, instead of a method declaration.

Nullable Reference Types

This feature will be very helpful to the developers to avoid NullReferenceException exceptions. Basically we will get a warning if any of the value is null. These warnings may be used to reveal the potential bugs in the code.

Let’s take a scenario to explain this feature. Consider the below class,

public class Person

{

       public string FirstName { get;  set; }

       public string MiddleName{ get; set; }

       public string LastName { get; set; }

       public Person (string first, string last)

      {

             FirstName = first;

             LastName = last;

      }

}

The above class has a constructor which accepts the first name and the last name as the parameter. Consider the below code snippet,

static class Program

{

       static void Main(string[] args)

       {

               Person person = new Person(“Michael”,”De Souza”); // middlename not defined

               int length = GetLengthOfMiddleName(person);

               Console.WriteLine(length);

        }

        static int GetLengthOfMiddleName(person)

       {

               return p.MiddleName.Length; //returns NullReferenceException

       }

}

The above code will be compiled successfully. But during runtime we will get a NullReferenceException because in the constructor we are not defining the middle name. In C# 8.0, this can be sort out by adding an attribute [NonNullTypes] at the respective class.

[module:NonNullTypes]

public class Person

{

      .   .   .   .   .  .  .

}

By doing this, you’ll get an error in the Person constructor as Non-nullable property ‘MiddleName’ is uninitialized. So you need to initialize the middle name in the constructor with some default value like,

Middle Name = “John”

Or else if you want to give it as a null value you can initialize in the class with a null coalescing operator like,

public string? MiddleName { get; set; }

And in the function GetLengthOfMiddleName you’ll get a warning like Possible dereference of a null reference ‘Middle Name’. So you have to include a condition to check whether the middle name is null or not. This can be done with a basic if condition but we can also do the same thing using null-coalescing operator.

static int GetLengthOfMiddleName(person)

{

       return p.MiddleName?.Length ?? 0;

}

The above function returns 0 if middleName is null or it returns the length of the middleName.

So you can see how C# allows us to manage the null reference exception which will be very useful to avoid the nightmare null reference exception.

System.Span<T>

You can able to pass the reference of a variable by using ref keyword. So in C# 8.0 you can use the System.Span<T> value type to pass array as an reference.

Consider the example,

static void main(string[] args)

{

       int[] array = new int[10];

       Span<int> span = array.AsSpan();

       for(int i = 0; I < 10; i++) array[i] = I; //array elements will also be copied to span

       foreach(int v in  span) Console.WriteLine(v);

}

array.AsSpan() will copy the reference of array to span variable. So, output of the above program will print the elements that are present in array.

You can also able to take some required elements from the span using Slice(int start) or Slice(int start, int length). The start parameter describes the index and the length describes the number of elements from the start index.

Span<int> slice = span.Slice(3,5));

foreach(int v in  slice) Console.WriteLine(v);

The above code snippet will print the values from 3 to 7. You can also able to add elements to the span by just assigning the specific index to the respective value. For example,

slice[2] = 43;

foreach(int v in  slice) Console.WriteLine(v);

So the output of the above program will be 0 1 2 3 4 43 6 7 8 9. It will also be inserted into the array because we are passing the reference of the array and making changes to the slice.

So another exciting feature introduced in C# 8.0 is you can easily able to get the range of elements from an array or else you can able to get an element from an specific index through the array by using ^ symbol. Consider the above span,

Console.WriteLine(span[^3]);

The above code snippet will print the third element from the last. You can also able to get a range of elements like,

Span<int> slice = span[3..^3];

So if you iterate and print slice, it will print from the index 3 to the third element from the last. You can also able to mention the start index and end index like,

Span<int> slice = span[3..7];

This will print the elements from the 3rd index to the 7th index.

Async streams and disposables

Async mechanism was introduced in C# 5.0. Basically async will return Task<T> which can be awaited to get the result. So in C# 8.0 you can able to use IAsnyncEnumerbale<T> which can be for eached to get a list of result.  Consider the below functionality,

IAsyncEnumerable<Person> people = database.GetPeopleAsync();

foreach await(var p in people) { …. }

In previous versions of C# you cannot able to make disposable async calls because we have only IDisposable interface which makes synchronous calls. So to make disposables async they have introduced IAsyncDisposable in C# 8.0. Consider the below functionality,

using await(IAsyncDisposable resource = await store.GetRecordAsync(…)) { …. }

More with Patterns

Patterns was introduced in C# 7.0. These patterns can either be used in an is expression or they can be used in a switch statement. Consider the below snippet of code,

var s = o switch                                                           //match expressions

{

int I                                          => $”Number {i}”,

Point(int x, int y)                   => $({x},{y})”,      //recursive patterns

string s when s. Length > 0 => s,

null                                          =>”<null>”,

  • => “<other>”

};

switch is an infix operator between o and the object that is going to change dynamically. So the switch does not contain any case keywords because we are just matching with the patterns. So the above code snippet will work like,

  • if s is an int à Call it as I àand return the number
  • If it’s a Point then deconstruct the point according to the deconstructor which was present in C# 7.0 then apply these nested patterns recursively to the values came out of the deconstructing that point. So it will return both x and y.
  • If it’s a string à It will check length > 0 à It will return the string.
  • If it’s null à it returns just null.
  • If there are no patterns matches with the above pattern it just returns other.

You can also able to match with tuple patterns such as,

state = match(state, request)             //tupple patterns

{

       (Closed, Open)      =>    Opened,

       (Closed, Lock)       =>     Locked,

       (Opened, Close)   =>     Closed,

       (Locked, Unlock)  =>     Closed,

} ;

Conclusion

These are some cool stuff’s introduced in C# 8.0. So, these improvements will make C# more robust and terse. I hope this blog would be very informative. Stay tuned for more on TechMeet360

Happy Coding! Cheers 😊

 

Author: Suhas Parameshwara

I'm a Passionate Full Stack Web Application Developer working at Kovai.Co. I like to explore new technologies and also to enhance the knowledge of other developers by writing blogs.

Get notified about any future events

Interested in learning more about TechMeet360 or knowing about any future events? Sign up below to get notified.

Back to Top