New features introduced with Microsoft .NET 7
Introduction
Microsoft has just released .NET 7 on 14th November 2022. We will look into some new features that have been introduced with C# in the latest version of .NET with practical examples. Let us begin.
Improved way to handle string literals
Let us create a console application using Visual Studio 2022 Community edition. Now, add the below code in the Program.cs file:
// Declare a string literal with quote
?//.NET 6
var firstValue = "<?xml version = \"1.0\"?>";
?
?
//.NET 6
var secondValue = @"<?xml version = ""1.0""?>";
?
?
//.NET 7
var thirdValue = """<?xml version = "1.0"?>""";
?
?
Console.WriteLine(firstValue);
Console.WriteLine(secondValue);
Console.WriteLine(thirdValue);
Run the application and you will see the below output:
Here, you see that the string literal with quotes have been displayed on the console three times. The first two methods were available in .NET 6 and earlier. These required some modifications to the text itself. However, in .NET 7 you do not need to make any updates within the string itself. Simply, adding three quotes makes the string useable. If your string contains three quotes, just change the prefix to four quotes. This simplifies the handling of XML and JSON type strings.
?
The file access modifier keyword
Let us create a console application using Visual Studio 2022 Community edition. Now, add a new class as below:
Next, add the below code to the new class:
namespace DotNet7File
{
??? public class FileTest
??? {
??????? public void PrintDateCall()
??????? {
??????????? Console.WriteLine("Printed from the FileTest.cs file");
??????? }
?????
??? }
}
Then, add another class as below:
And add the below code to it:
namespace DotNet7Fil
{
??? public class FileTest
??? {
??????? public void PrintDateCall()
??????? {
??????????? Console.WriteLine("Printed from the AnotherFileTest.cs file");
??????? }
?
??? }
}
You will see the below errors:
The reason is that the same class has already been defined in this namespace. Now change the code in the second file to the below:
namespace DotNet7Fil
{
??? file class FileTest
??? {
??????? public void PrintDateCall()
??????? {
??????????? Console.WriteLine("Printed from the AnotherFileTest.cs file");
??????? }
?
??? }
}
All will compile fine. This is because the scope of the class in the second file has been changed to file and hence it is accessible at the file level only.
?Add the below code to the “Program.cs” file:
using DotNet7File
?
FileTest fileTest = new ();
?
fileTest.PrintDateCall();
?Run the application and you will see the below output:
As expected, the class with the public modifier was created and called. So, how do we call the file level class. It can be called from within the file. Change your code in the second file as below:
namespace DotNet7Fil
{
??? file class FileTest
??? {
??????? public void PrintDateCall()
??????? {
??????????? Console.WriteLine("Printed from the AnotherFileTest.cs file");
??????? }
??? }
?
??? public class ToCallExternally
??? {
??????? public void CallTheFileLevelClass()
??????? {
??????????? FileTest fileTest = new();
??????????? fileTest.PrintDateCall();
??????? }
??? }
?
}
And update the code in the Program.cs file as below:
using DotNet7File
?
ToCallExternally toCallExternally = new ();
?
toCallExternally.CallTheFileLevelClass();
Now, run the application and you will see the below:
领英推荐
Here, we see the class at the file level is used.
The generic number interface feature
Let us create a console application using Visual Studio 2022 Community edition. Now, add the below code to the “Program.cs” file:
using System.Numerics
?
double[] values = new[] { 100, 50, -75, 102.50, -77.50 };
?
Console.WriteLine(AddAllNumbers(values));
?
?
T AddAllNumbers<T>(T[] numbers) where T : INumber<T>
{
?
??? T sumOfPositiveNumbers = T.Zero;
?
??? foreach(var number in numbers)
??? {
??????? if (T.IsPositive(number))
??????? {
??????????? sumOfPositiveNumbers += number;
??????? }
??? }
?
??? return sumOfPositiveNumbers;
};
?When you run this application, you get the below:
Here, we see that we have created a generic function to add all values of an array that are positive. For this we are using the new “INumber” interface. In this way we can handle both integer and double values and hence we do not need to maintain two different functions for these two types.
?The generics pattern matching feature
Let us create a console application using Visual Studio 2022 Community edition. Now, add the below code to the “Program.cs” file:
// Generic list of string typ
var genericList = new List<string>() { "Munib","Butt" };
?
Console.WriteLine(genericList is ["Munib", "Butt"]);
Console.WriteLine(genericList is ["John", "Smith"]);
?
?
// Generic list of int type
var genericListIntegers = new List<int>() { 100,200,300,400 };
?
Console.WriteLine(genericListIntegers is [100, 200, 300, 400]);
Console.WriteLine(genericListIntegers is [>99, >199, >299, >399]);
Console.WriteLine(genericListIntegers is [> 99, > 199, > 299, > 499]);
When you run this application, you get the below:
Here, we see that we have created two generic lists. The first one is of string type and we assigned two values to it. Then we matched the values. In the first case, we get True because the values matched the pattern. However, in the second case we get False as the values did not match the pattern. To better explain the use of pattern matching, we can look at the second generic list which is of Int type. Here we see that we can match the exact values or even conditions. If all match correctly, we get a True result or else false. In the last pattern matched, the last value is “400” which does not satisfy the condition for this element which states that it must be “>499” and hence whole pattern match returns a False.
The required keyword
Let us create a console application using Visual Studio 2022 Community edition. Now, add the below code to the “Program.cs” file:
var employee = new Employee { Id = 1 }
?
var student = new Student();
?
?
class Employee
{
? public int Id { get; init; }
? public required string Name { get; init; }
?
}
?
class Student
{
??? public int Id { get; set; }
??? public required string Name { get; set; }
?
};
Here, we see that we have created two classes and have defined the property “Name” with the new “required” keyword. This means that this property is mandatory for this class. If we do not add it and try to compile the code, we get the below:
Now, change the code to the below:
using System.Diagnostics.CodeAnalysis
?
var employee = new Employee { Id = 1, Name="Munib Butt" };
var student = new Student(1,"Munib Butt");
?
?
class Employee
{
? public int Id { get; init; }
? public required string Name { get; init; }
?
}
?
class Student
{
??? public int Id { get; set; }
??? public required string Name { get; set; }
?
??? [SetsRequiredMembers]
??? public Student(int id, string name)
??? {
??????? Id = id;
??????? Name = name;
??? }
?
};
All compiles fine. Hence, the properties defined as “required” need to be defined in the object initializer or attribute constructor otherwise the code does not compile. This feature is very useful for adding properties that are required.
?The multi-line string literal
Let us create a console application using Visual Studio 2022 Community edition. Now, add the below code to the “Program.cs” file:
var firstName = "John"
var lastName = "Smith";
?
Console.WriteLine($"This is a testing string literal with {
??? firstName} and {
??? lastName}");;
When we run the application, we see the below:
Hence, in .NET 7/ C#11, we can break the string literal into multiple lines in order to improve readability. However, when it is displayed it will show on one line.
Summary
In this article we looked at some new features introduced with .NET 7. Using these features can improve our code quality and enhance performance and readability.