In this post I will be showing you today whats new in C# 9 and some of the exciting features that we now got access to .
The 3 topics we will cover today are Top Level Calls, Records and Init Setters
You can also watch the full step by step video on Youtube:
Source code:
https://github.com/mohamadlawand087/v12-Csharp9NewFeatures
So what will need, to get started either
- Visual Studio 2019 Community 16.8.1
- You need .Net 5 installed and use Visual Studio Code
So lets get started, we will start by creating a console application from our terminal
dotnet new console -n "SampleFeature"
Once our application has been created let us open it in our IDE and build it to make sure everything is working. Then we need to check if the target framework of our application is targeting .Net 5
Click on SampleFrature.csproj and we will see the TargetFramework is 5.0
Now we will start by discovering TLC
Top level calls
Lets add a new function to our application called GetMyName and will call it from our main function
GetMyName("Mohamad");
private static string GetMyName(string name)
{
return $"Hello {name}, welcome to C# 9";
}
Now lets run the application and see it in action.
The goal of Top level calls is to simplify this code and we can transform our application to something like this
using System;
Console.WriteLine("Hello World from TLC");
Console.WriteLine(GetMyName("Mohamad"));
static string GetMyName(string name)
{
return $"Hello {name}, welcome to C# 9";
}
- if we have a class file that contain top level calls which means no namespace, no class not methods just pure code.
- a method modifier cannot be public/private we can only call methods inside our TLC.
- Top level call needs to be on top, and TLC needs to be in 1 place only we have it in 2 classes it will fail
C# 9 will assume this is the entry point to our application
Why use TLC
- simplicity
- quick to create
- very simple to use in a none windows environment
- easily created from within the terminal
Records
First lets create a new console application
dotnet new console -n "RecordsSample"
New way C# to define class types, specifically for types that act as values not as traditional objects.
Traditional objects are a way to encapsulate our classes and object to hide it from the consumer and the only way to access it is via methods. So for example if we change the entire implementation of a class the user will not know about it as they are just accessing it via the methods and property.
Value style objects is the opposite of this implementation, their behaviour is only define by their state. For example in a Json object all of the information is in the obj itself there is no need for methods to get access to any information, we just access the object and its properties, these objects expose their state.
These object has common set of behaviours,
- they want equality so 2 objects are the same
- deconstruction
- C# patern matching
- Enumerability
The traditional object when they are used as a value object their cost to performance is relatively low but once we start adding everything up from Json, Rest API, Ef models, helpers obj it really starts to add up.
Lets see an example
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var p1 = new Person("Mohamad", "Lawand");
var p2 = new Person("Slim", "shady");
var json = JsonSerializer.Serialize(p1);
Console.Write(json);
var p3 = JsonSerializer.Deserialize<Person>(json);
var isEqual = p3 == p1;
Console.WriteLine($"Is Equal p1 == p3 {isEqual}");
var (firstName,lastName) = p1;
Console.WriteLine($"{lastName}, {firstName}");
Console.WriteLine($"Is Mohamad in chat {IsInChat(p2)}");
}
static bool IsInChat(Person p) => p switch
{
("Mohamad", "Lawand") => true,
("Slim", "Shadt") => true,
_=>false
};
}
// in C# 8 -- We have to write all of this boiler plate code
public class Person : IEquatable<Person>
{
public Person(string fname, string lname)
{
FirstName = fname;
LastName = lname;
}
public string FirstName { get; set; }
public string LastName { get; set; }
public void Deconstruct(out string firstName, out string lastName)
{
firstName = FirstName;
lastName = LastName;
}
public override bool Equals (object obj) => obj is Person p && Equals(p);
public bool Equals(Person other) =>
other is object &&
FirstName == other.FirstName &&
LastName == other.LastName;
public override int GetHashCode() => HashCode.Combine(FirstName, LastName);
public override string ToString() => $"{FirstName} {LastName}";
}
// In C# 9
record Person (string FirstName, string LastName);
When we add the record we convert all of the boilerplate code that we wrote into a single line
Immutability aspect of records so in C# by default is a mutable language where there is certain construct where we can opt in to immutability like read only structs, read only fields ...
But in records immutability is by default so for example
var p1 = new Person("Mohamad", "Lawand");
p1.FirstName = "John"; // this will not work since there is no setters
// The way we do this immutable objects - we create copy of obj and we
// update the states in the new copy
// in C# 9 records we do this something called withords
// how is works, it takes p1 and makes a copy change the first name return the obj
// its the same exact same construct as obj initialize
p1 = p1 with { FirstName = "John" };
if we want to change a record from immutable to mutable we need to update our record to the below so it will be mutable
record Person (string FirstName, string LastName)
{
public string FirstName {get;set;} = FirstName;
public string LastName {get;set;} = LastName;
}
So in summary records are just collection of values that we are attaching a common set of behaviours and of these behaviours is equality
Init Setters
First lets create a new console application
dotnet new console -n "InitSamples"
We will simply show how to initialise objects in C#8 and C# 9 and see the difference, this feature is more of a organisation feature which will allow us to better structure our code.
// C# 8
public class Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
}
var p1 = new Point(10,23);
// This was not possible the only way it was done via the constructor
var p2 = new Point
{
X = 42,
Y = 30
};
// C# 9
public class Point
{
public int X { get; init;}
public int Y { get; init;}
}
// This is now functional in C# 9 only when constructing an obj and i don't
// need to have the constructor
var p2 = new Point
{
X = 42,
Y = 30
};
// If i try to change the objects after initialization
p2.X = 50; // this will cause an error since the obj has already been initalized and its immutbale
Thank you for reading the article, please like and share it.
Top comments (0)