As a programming language, C# has gone through several iterations, and with each new version, we see new features and enhancements that make programming with it even better.
C# 10, the latest language version, is no exception.
In this article, we will explore the new features of C# 10 and provide examples of how to use them in your code.
C# 10 is supported by .NET 6 and newer framework versions.
You can download Visual Studio 2022, which comes with the .NET 6 SDK.
Table of Contents
C# 10 New features
The following is a list of some of the new features in C# 10.
Global using directives
C# 10 introduces a new feature called global using directives. This feature allows you to specify using directives applied to all files in a project. It can be useful if you have a set of commonly used namespaces you want to include in all your files without adding them manually on the top of each C# file.
For example, if you want to use the System namespace in all your files, you can add a global using directive in your project file like this:
It is advised that you keep your global namespaces in a separate file. We have created a GlobalUsing.cs
file, which looks like this:
// GlobalUsings.cs
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Threading.Tasks;
global using Microsoft.AspNetCore.Mvc;
Implicit Global Usings
Implicit usings available in .NET 6/ C# 10 are used to add common global using directives for the specific types of projects.
Developers must set the ImplicitUsings
attribute in the .csproj
file to allow implicit using.
When you enable the ImplicitUsings
property, it imports a number of common namespaces from the .NET
class library and makes them available for usage in C# files.
<ImplicitUsings>enable</ImplicitUsings>
When you create a new .NET 6 project with Visual Studio 2022, this new property is enabled by default:
The implicit using is a hidden auto-generated file that declares global using statements behind the scenes in your obj/Debug/net6.0 folder. The name of the file is “ProjectName.GlobalUsings.g.cs“
In my case, when I open this file, I find the following content inside:
// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;
In the following example, we created the console application in 64-bit Visual Studio 2022. You can see that it allows printing the message without importing any (using) directives.
Here’s an example of a Program.cs
file created in C# 10
Note: Starting with C# 9, you don’t have to explicitly include the Main method in a console application project. Instead, the compiler produces a class and the Main method as an entry point for the application.
Record structs
Record structs are a new feature introduced in C# 10 that allows you to create value types that behave like reference types.
Record structs are immutable by default, meaning you cannot modify their properties once they are created. They also have a built-in GetHashCode
and Equals method, making them ideal for data structure use.
Records are implicitly inherited from the System.ValueType
.
Structs already have value equality, so you can compare them based on their value. Record structs now have the == operator and the IEquatable<T>
interface.
- Performance: Record Structs include a special implementation of IEquatable <T> and a ToString() override method to prevent performance issues of reflection.
- Limitation: Record struct parameters cannot use ref, out or this modifiers (but in and params are allowed).
- Differences: The only major difference between a regular record and a record struct in C# 10 is that a regular record passes by reference from function to function, whereas a record struct is copied by its values.
- Mutable: By default, the properties of the record struct are mutable or changeable (get/set), whereas the properties of the record class are immutable. Even so, a readonly record struct in C# 10 can be declared that satisfies the semantics of the record class and is immutable.
You can define record struct types in C# 10 and later by using either positional parameters or standard property syntax:
The record structs can be Positional with a default constructor that declares public members implicitly:
// Positional syntax for record struct
public record struct Person(string FirstName, string LastName);
// Person person = new Person { FirstName = "Shekh", LastName = "Ali"};
You may also use the readonly modifier to make a record struct immutable.
public readonly record struct Person(string name, int age);
Mutable property of record type in C# 10
In previous versions of C#, record types were immutable by default, which meant that you could not change their properties once they were created. C# 10 introduces a new feature that allows you to mark properties in a record as mutable.
To mark a property as mutable, you can use the init
keyword like this:
public record Person(string FirstName, string LastName)
{
public string Nickname { get; init; }
}
Record type with positional parameters
public record struct Point(int X, int Y);
Example: record struct in C# 10
An example of a record struct in C# 10 can be seen below.
File-scoped namespace declaration
C# 10 introduces a new feature called file-scoped namespace declarations. This feature allows you to declare a namespace limited to a single file.
In C # 10, a new form of namespace declaration is available. You can now include the file-scoped namespace as a statement, followed by a semicolon “;” without the curly braces {}.
For example, you can declare a file-scoped namespace like this:
// File-scoped namespace declaration
namespace MyConsoleApp;
There can only be one file-scoped namespace declaration, which must come before any type. It reduces the code’s complexity and eliminates a level of nesting.
The new syntax for declaring File-scoped namespaces saves both horizontal and vertical space.
Extended property patterns
In C# 10, you can now use extended property patterns to match objects based on their properties and their values. It means that you can use more complex expressions to match objects.
For example, you can use the following code to match an object that has a property called Name and a property called Age that is greater than 18:
Example 1:
public record Person(string FirstName, string LastName, int Age);
var person = new Person("Shekh", "Ali", 25);
if (person is { FirstName: "Shekh", Age: > 18 })
{
Console.WriteLine("This person is over 18 years old");
}
Extended property patterns are included in C# 10 to make it much simple and easier to access nested property values in patterns.
If we add an Address to the Person record, we may pattern match in both of the following ways:
Example: Extended property patterns
namespace MyApp;
class Address
{
public string City { get; set; }
}
internal class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Address Address { get; set; }
}
public class Program
{
public static void Main()
{
var obj = new Person
{
Name = "Shekh",
Age = 29,
Address = new Address { City = "Delhi" }
};
Console.Write($" Name : {obj.Name} Age: {obj.Age}");
if (obj is Person { Address: { City: "Mumbai" } })
Console.Write($"City: { obj.Address.City}");
if (obj is Person { Address.City: "Delhi" }) // Extended property pattern
Console.Write($" City: { obj.Address.City}");
Console.ReadKey();
}
}
Improvements of structure types
C# 10 introduces several improvements to structure types. One of the main improvements is that you can now use the ‘with’ keyword to create a new instance of a structure with updated values.
The following example shows the result of a with expression returns a new instance with the updated value.
Sealed modifier on ToString() in record types
In previous versions of C#, record types automatically generated a ToString()
method that provided a string representation of the object. In C# 10, you can now mark this method as sealed, which prevents derived classes from overriding it.
The following example demonstrates a record called Person that implements an override of the ToString() method with the sealed keyword.
The sealed keyword prevents the compiler from synthesizing a ToString()
method for any derived record types.
The Employee record inherits from Person and tries to override the ToString()
method, resulting in the following compilation error:
Example:
public record Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
public sealed override string ToString()
{
return $"FirstName: {FirstName} LastName{LastName}";
}
}
public record Employee : Person
{
// Trying to overide ToString() in the derived record type.
// Error CS0239 'Employee.ToString()': cannot override inherited member
// 'Person.ToString()' because it is sealed.
public override string ToString()
{
return $"FirstName: {FirstName} LastName{LastName}";
}
}
Null Parameter Checks in C# 10
C# 10 introduces a new feature that allows you to perform null checks on method parameters. This feature can help you avoid null reference exceptions in your code.
For example, you can use the following code to check if a parameter is null:
void ValidateMail(string email)
{
if (email == null)
{
throw new ArgumentNullException("email");
}
}
In the code above, we have a function with only one parameter, “email.” If the parameter email is null, we must throw an ArgumentNullException.
In C# 10, to simplify the above problem ArgumentNullException.ThrowIfNull and !! two exclamation marks next to the parameter was introduced.
Example 1: C# 10 Null checks parameter ( ThrowIfNull )
string? email =null;
ValidateMail (email);
void ValidateMail(string email)
{
//if(email == null)
//{
// throw new ArgumentNullException ("email");
//}
ArgumentNullException.ThrowIfNull(email);
Console.WriteLine($"Email : {email}");
}
Example 2: Null checks with two exclamation marks
void PersonDetail(Person personObject !!)
{
// Code here
}
The personObject
is automatically checked for null in the above code, and if it is null, the AgrumentNullException is thrown.
References: devblogs- Welcome to C# 10, MSDN- What’s new in C# 10
Thank you for taking the time to read the blog, if you find it interesting, please like, comment, and share it with others. Thanks.
FAQs:
Q: Is it possible to use record types with mutable properties?
Yes, in C# 10, you can mark properties in a record as mutable using the init keyword.
Q: What is the benefit of using file-scoped namespace declarations?
File-scoped namespace declarations can help you organize your code and make it easier to understand.
Q: Can you explain how to use extended property patterns in C# 10?
Extended property patterns allow you to match objects based on their properties and their values. You can use complex expressions to match objects.
You might also like:
- C# String Interpolation – Syntax, Examples, and Performance
- C# String Vs StringBuilder
- Singleton Design Pattern in C#: A Beginner’s Guide with Examples
- SOLID Design Principles in C#: A Complete Example
- 10 Difference between interface and abstract class In C#
- C# Queue with examples
- Web API vs web service: Top 10+ Differences between web API and web service in C#
- C# Polymorphism: Different types of polymorphism in C# with examples
- C# Stack
- Understanding the Difference between == and Equals() Method in C#
- Difference between SortedList and SortedDictionary in C#
- C# Hashtable vs Dictionary vs HashSet
- C# stack vs heap
- C# Dictionary with Examples
- Format String in C#: A Comprehensive Guide to Understand String Formatting in C#
- Fibonacci sequence: Fibonacci series in C# (with examples)
Let others know about this post by sharing it and leaving your thoughts in the comments section.
- Difference Between Array And ArrayList In C#: Choosing the Right Collection - May 28, 2024
- C# Program to Capitalize the First Character of Each Word in a String - February 21, 2024
- C# Program to Find the Longest Word in a String - February 19, 2024