New features in C# 7.0
C# is the most popular language in the .NET ecosystem and was first introduced with the .NET Framework in 2002. The current stable version of C# is 7. The following chart shows how C# 7.0 has progressed and what versions were introduced in different years:
Here are some of the new features that were introduced with C# 7.0:
- Tuples
- Pattern matching
- Reference returns
- Exceptions as expressions
- Local functions
- Out variables Literals
- Async Main
Tuples
Tuples solve the problem of returning more than one value from a method. Traditionally, we can use out variables that are reference variables, and the value is changed if they are modified from the calling method. However, without parameters, there are some limitations, such as that it cannot be used with async
methods and is not recommended to be used with external services.
Tuples have the following characteristics:
- They are value types.
- They can be converted to other Tuples.
- Tuple elements are public and mutable.
A Tuple is represented as System.Tuple<T>
, where T
could be any type. The following example shows how a Tuple can be used with the method and how the values can be invoked:
static void Main(string[] args) { var person = GetPerson(); Console.WriteLine($"ID : {person.Item1}, Name : {person.Item2}, DOB : {person.Item3}"); } static (int, string, DateTime) GetPerson() { return (1, "Mark Thompson", new DateTime(1970, 8, 11)); }
As you may have noticed, items are dynamically named and the first item is named Item1
, the second Item2
, and so on. On the other hand, we can also name the items so that the calling party should know about the value, and this can be done by adding the parameter name for each parameter in the Tuple, which is shown as follows:
static void Main(string[] args) { var person = GetPerson(); Console.WriteLine($"ID : {person.id}, Name : {person.name}, DOB : {person.dob}"); } static (int id, string name, DateTime dob) GetPerson() { return (1, "Mark Thompson", new DateTime(1970, 8, 11)); }
Note
To learn more about Tuples, please check the following link:https://docs.microsoft.com/en-us/dotnet/csharp/tuples.
Patterns
Patterns matching is the process of performing syntactical testing of the value to verify whether it matches the certain model. There are three types of patterns:
- Constant patterns.
- Type patterns.
- Var patterns.
Constant pattern
A constant pattern is a simple pattern that checks for the constant value. Consider the following example: if the Person
object is null, it will return and exit the body
method.
The Person
class is as follows:
class Person { public int ID { set; get; } public string Name { get; set; }
public DateTime DOB { get; set; } }
In the preceding code snippet, we have a Person
class that contains three properties, namely ID
, Name
, and DOB
(Date of Birth).
The following statement checks for the person
object with a null constant value and returns it if the object is null:
if (person is null) return;
Type pattern
The type pattern can be used with an object to verify whether it matches the type or suffices the expression based on the conditions specified. Suppose we need to check whether the PersonID
is int
; assign that ID
to another variable, i
, and use it in the program, otherwise return
:
if (!(person.ID is int i)) return; Console.WriteLine($"Person ID is {i}");
We can also use multiple logical operators to evaluate more conditions, as follows:
if (!(person.ID is int i) && !(person.DOB>DateTime.Now.AddYears(-20))) return;
The preceding statement checks whether the Person.ID
is null or not and whether the person is older than 20.
Var pattern
The var pattern checks if the var
is equal to some type. The following example shows how the var
pattern can be used to check for the type and print the Type
name:
if (person is var Person) Console.WriteLine($"It is a person object and type is {person.GetType()}");
Note
To learn more about patterns, you can refer to the following link: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#pattern-matching.
Reference returns
Reference returns allow a method to return an object as a reference instead of its value. We can define the reference return value by adding a ref
keyword before the type in the method signature and when returning the object from the method itself.
Here is the signature of the method that allows reference returns:
public ref Person GetPersonInformation(int ID); Following is the implementation of the GetPersonInformation method that uses the ref keyword while returning the person's object. Person _person; public ref Person GetPersonInformation(int ID) { _person = CallPersonHttpService(); return ref _person; }
Expression bodied member extended
Expression bodied members were introduced in C# 6.0 where the syntactical expression of the method can be written in a simpler way. In C# 7.0, we can use this feature with a constructor, a destructor, an exception, and so on.
The following example shows how the constructor and destructor syntactic expressions can be simplified using expression bodied members:
public class PersonManager { //Member Variable Person _person; //Constructor PersonManager(Person person) => _person = person; //Destructor ~PersonManager() => _person = null; }
With properties, we can also simplify the syntactic expression, and the following is a basic example of how this can be written:
private String _name; public String Name { get => _name; set => _name = value; }
We can also use an expression bodied syntactic expression with exceptions and simplify the expression, which is shown as follows:
private String _name; public String Name { get => _name; set => _name = value ?? throw new ArgumentNullException(); }
In the preceding example, if the value is null, a new ArgumentNullException
will be thrown.
Creating Local Functions
Functions that are created within a function are known as Local Functions. These are mainly used when defining helper functions that have to be in the scope of the function itself. The following example shows how the factorial of the number can be obtained by writing a Local Function and calling it recursively:
static void Main(string[] args) { Console.WriteLine(ExecuteFactorial(4)); } static long ExecuteFactorial(int n) { if (n < 0) throw new ArgumentException("Must be non negative", nameof(n)); else return CheckFactorial(n); long CheckFactorial(int x) {
if (x == 0) return 1; return x * CheckFactorial(x - 1); } }
Out variables
With C# 7.0, we can write cleaner code when using out
variables. As we know, to use out
variables, we have to first declare them. With the new language enhancement, we can now just write out
as a prefix and specify the name of the variable that we need that value to be assigned to.
To clarify this concept, we will first see the traditional approach, which is shown as follows:
public void GetPerson() { int year; int month; int day; GetPersonDOB(out year, out month, out day); } public void GetPersonDOB(out int year, out int month, out int day ) { year = 1980; month = 11; day = 3; }
And here with C# 7.0, we can simplify the preceding GetPerson
method, which is shown as follows:
public void GetPerson() { GetPersonDOB(out int year, out int month, out int day); }
Async Main
As we already know, in .NET Framework, the Main
method is the main entry point from where the application/program is executed by the OS. For example, in ASP.NET Core, Program.cs
is the main class where the Main
method is defined, which creates a WebHost
object, runs the Kestrel server, and loads up the HTTP pipeline as configured in the Startup
class.
In the previous version of C#, the Main
method had the following signatures:
public static void Main(); public static void Main(string[] args); public static int Main(); public static int Main(string[] args);
In C# 7.0, we can use Async Main to perform asynchronous operations. The Async/Await feature was initially released in .NET Framework 4.5 in order to execute methods asynchronously. Today, many APIs provides Async/Await methods to perform asynchronous operations.
Here are some additional signatures of the Main
method that have been added with C# 7.1:
public static Task Main(); public static Task Main(string[] args); public static Task<int> Main(); public static Task<int> Main(string[] args);
Because of the preceding async signatures, we can now call async
methods from the Main
entry point itself and use await to perform an asynchronous operation. Here is a simple example of ASP.NET Core that calls the RunAsync
method instead of Run
:
public class Program { public static async Task Main(string[] args) { await BuildWebHost(args).RunAsync(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .Build(); }
Async Main is a feature of C# 7.1, and to enable this feature in Visual Studio 2017, you can go to the project properties, click on the Advance
button and set the Language version
as C# latest minor version (latest)
, which is shown as follows: