visit
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!");
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" };
// 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 });
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) { ... }
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)...
}
void
return type.
ref
or out
parameters.
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:
ref
or out
.
non-void
return types
private
, public
, etc ..).
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) { ... }
}
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"
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; }
}
// 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;
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.
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)
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);
}
// equivalent to previous one
public data class Person(string FirstName, string LastName);
var person = new Person("Ahmed", "Yousif"); // positional construction
var (fName, lName) = person; // positional deconstruction