Introduction to C# 10 - Part 2
This post is a continuation of the previous blog on Introduction to C# 10 - Part 1. In the previous blog, we had seen features like Global using and Implicit using, File-scope namespace, Extended property patterns, etc. In this blog, we will look into some more powerful features of C#. Without further ado, let’s begin.
Table of Contents
- Variable Declaration and Assignment
- Type of Lambda Expression
- Explicit return types for Lambda Expression
- Ability to add attributes to Lambda Expression
- Parameterless constructor for a struct
- Record type for struct
- Natural type for method groups
- Constant interpolated strings
- Performance improvement for interpolation
- Conclusion
Variable Declaration and Assignment
Before the introduction of C# 10, in order to declare and assign multiple variables, the variables had to be either created together or had to be created before and then assign value to them together.
For example -
(int a, int b) = (0, 1);
In the above example, both variable declaration and assignment have been done in a single line.
int a = 0;
int b = 0;
(a, b) = (0, 1);
In the above example, a and b variables are declared separately before values get assigned to them.
However, in C# 10, variable declaration and assignment of multiple variables can happen at any instance.
Int a = 0;
(a, int b) = (0, 1);
Type for Lambda Expression
Developers commonly use lambda expressions to achieve various programming needs. Before C# 10, there were no types assigned for the lambda expressions. In other words, if we use a var as the variable type for the response from a lambda expression, it would throw a compiler error.
For example,
string s = "234" ;
var parse = (s) => int.Parse(s);
The above code would throw a compiler error stating - cannot assign lambda expression to an implicitly typed variable.
It expects the type of the variable to be Func<string, int>. With C# 10, implicit variable assignment is possible for lambda expressions. Writing the above code in C# 10 will result in the implicit variable assignment of type Func<string, int>. This means the compiler infers the type and takes up Func or Action delegate based on availability.
Though the type gets assigned implicitly in C# 10, it is also possible to assign a less explicit type such as an object to it.
Explicit return types for lambda expressions
Developers write a lot of lambda queries when working with collections and ORM-based database connecting frameworks. For example,
List<string> inputData = new List<string>();
var result = inputData.Select(x =>x);’
But, sometimes there would be a need to send two different sets of results based on the validation outcome.
For example, if the string in the above code is null, then you need to send some data and when it's not null, you need to send some other data.
From C# 10, it is possible to explicitly provide the return type of a lambda expression as below.
List<string> abc = new List<string>();
var result = abc.Select((x) => (String.IsNullOrEmpty(x)) ? false : x);
Developers would have to use attributes on top of classes/methods to achieve various functionalities through a common code. The same can be applied to Lambda expressions as well.
Func<string, int> parse = [Example(1)] (s) => int.Parse(s);
Parameterless constructor for a struct
Before C#10, a struct could not hold parameterless constructors, and the fields of the structs were set to the default value. C#10 provides the option to have parameterless constructors for struct, similar to that of class. Using this, the developers can set the required values for the fields.
public struct Person
{
public Person()
{
Name= "<unknown>";
}
public string Name { get; init; }
}
It can also be initialized like,
public struct Person
{
public string Name { get; init; } = "<unknown>";
}
However, while initializing, if they are created with default(Person), then the parameterless constructor will be ignored and all the field values will be set to default. The same applies to arrays of structure initialization as well.
Record type for struct
From C# 9, record types were introduced for classes where an immutable property could be created with reference types with the help of the init keyword. A similar feature is introduced for a struct in C# 10, where developers can declare -
public record struct Address
{
public string City{ get; init; }
public string State { get; init; }
}
Defining a public record name/public record class name will still be identified as a record class by the compiler. To create a record struct, a public record struct name has to be provided.
Also, it is positional where declaring with the below syntax will also create a record struct.
public record struct Address(string City, string State );
And to create an immutable record struct with positional syntax, then the following code can be used.
public readonly record struct Address(string City, string State);
Similar to the record class, the ‘with’ keyword can be used to modify particular fields of it.
var address = new Address { City = "SomeCity" };
var updatedData = address with { State = "SomeState" };
Natural type for method groups
Method names without argument lists (referred to as method groups) can now have natural types, provided if they have just one overload.
var read = Console.Read;//Valid as it has only one overload.
var write = Console.Write;//Invalid because it has more than one overload.
Constant interpolated strings
From C#10, it is possible to create an interpolated resultant string that can be of type constant with all the interpolation variables being constant inside it.
var city = "SomeCity";
const string dd = $"City is {nameof(city)}";
If you look at the above example, an interpolated string has been assigned to a constant variable.
Performance improvement for interpolation
Interpolation is a very useful technique that can be used for formatting strings as required. But, we never know what happens and how it processes the data. So far, in string interpolation, the string.format is indirectly called. This can lead to loads of memory usage in terms of boxing of arguments, allocation of an argument array, and the resultant string.
From C# 10, an interpolated string handler will be internally overloaded for the methods and when an interpolated string is passed as an input parameter, the compiler recognizes it as a string interpolation handler instead of a string. And it works by appending strings individually to the output. For example, if we have the below code,
var sb = new StringBuilder();
sb.Append($"Hello {args[0]}, how are you?");
With the new string interpolation handler, ‘hello’ will be first appended to the string builder, then the array argument, finally followed by the remaining content.
Conclusion
And that concludes the list of top features of C# 10 that have made the lives of millions of developers across the world even simpler.