C # 7 adds a number of new features and focuses on data consumption, simplifying code and performance. The biggest features have already been considered -
tuples ,
local functions ,
pattern matching and
throw expressions . But there are other new opportunities, both large and small. All of them are combined to make the code more efficient and understandable so that everyone will be happy and productive.
Let's look at the other features of the new version of C #. So! Let's start!
Out variables
Currently, in C #, using
out parameters is not as flexible as we would like. Before calling a method with
out parameters, you first need to declare the variables to pass to the method. Since usually these variables are not initialized (since they will be rewritten by the method), you cannot use
var to declare them, therefore you must specify the full type:
')
public void PrintCoordinates(Point p) { int x, y;
In C # 7,
out variables are entered. They allow you to declare variables right on the spot where they are passed as
out arguments :
public void PrintCoordinates(Point p) { p.GetCoordinates(out int x, out int y); WriteLine($"({x}, {y})"); }
Notice that the variables rise to the outer scope in the closing block, so the subsequent line can use them.Since
out variables are declared directly as arguments to
out parameters, the compiler can infer their type (if there are no conflicting overloads), so you can use
var instead of type when declaring:
p.GetCoordinates(out var x, out var y);
Typically,
out parameters are used in
Try ... patterns, where a logical return value indicates success, and
out parameters carry the results obtained:
public void PrintStars(string s) { if (int.TryParse(s, out var i)) { WriteLine(new string('*', i)); } else { WriteLine("Cloudy - no stars tonight!"); } }
You can also use “
wildcards ” in
out parameters in the form of
_ to ignore
out parameters that are not needed:
p.GetCoordinates(out int x, out _);
Please note that you can omit the type declaration with “
wildcards ”.
string inputDate = ""; if (DateTime.TryParse(inputDate, out DateTime dt)) {
The question is, what if the conversion of a string to a
DateTime variable is unsuccessful, and we still try to use the output value?
The variable will
have a default value .
Literal improvements
C # 7 allows you to use the “
_ ” character as a delimiter of digits inside numeric literals:
var d = 123_456; var x = 0xAB_CD_EF;
You can put this character anywhere between the numbers just as many times as necessary to improve readability. It does not affect the value.
The separator can be used with the types
byte ,
int ,
long ,
decimal ,
float and
double :
public const long BillionsAndBillions = 100_000_000_000; public const double AvogadroConstant = 6.022_140_857_747_474e23; public const decimal GoldenRatio = 1.618_033_988_749_894_848_204_586_834_365_638_117_720_309_179M;
In addition, C # 7 presents
binary literals . Now you can write the number in binary form.
var b = 0b101010111100110111101111;
0b at the beginning of a constant means that the number is written in binary format.
You can also use the delimiter character, which will undoubtedly improve the readability of the literal.
var b = 0b1010_1011_1100_1101_1110_1111;
Discharge separator cannot stand at the end or at the beginning of a literal
byte a = 0b_0000_0001;
Ref returns and locals
Just as you can pass method parameters by reference (with the
ref modifier), you can now also return them by reference, and also save them by reference in local variables.
public ref int Find(int number, int[] numbers) { for (int i = 0; i < numbers.Length; i++) { if (numbers[i] == number) { return ref numbers[i];
This is useful for returning specific fields from large data structures. For example, a game may contain its data in a large pre-distributed array of structures (to avoid pauses when garbage collection). Now methods can return a link directly to a structure through which the caller can read and modify it.
There are some security restrictions:
- You can only return links that are “safe to return”: only those that were given to you, and those that point to fields in objects
- Local ref variables are initialized at a specific storage location and cannot be changed to point to another
Returned generic types in async methods
Until now,
async methods in C # must either return
void ,
Task or
Task ‹T› . C # 7 allows you to define other types in such a way that they can be returned from the asynchronous method. The return type must still match the asynchronous pattern, which means that the
GetAwaiter method must be available. A concrete example: a new type
ValueTask has been added to the .NET Framework, allowing to use this new language feature
Since
Task and
Task ‹T are reference types, allocating memory in performance-affecting segments (especially when allocating memory in limited cycles) can seriously degrade performance. Support for generic types of return values allows you to return a small significant type instead of a reference type, thereby preventing excessive memory allocation.
class Program { static Random rnd; static void Main() { Console.WriteLine($"You rolled {GetDiceRoll().Result}"); } private static async ValueTask GetDiceRoll() { Console.WriteLine("...Shaking the dice..."); int roll1 = await Roll(); int roll2 = await Roll(); return roll1 + roll2; } private static async ValueTask Roll() { if (rnd == null) rnd = new Random(); await Task.Delay(500); int diceRoll = rnd.Next(1, 7); return diceRoll; } }
Conclusion
Use the new features of C #, because they facilitate development, save time and increase the readability of the code. This article ends the series of articles on the C # 7 version. All questions of interest, please in the comments or in a personal. I will answer you for sure. Thanks to all!