In this article I will go through the changes/ advancement of C# over the time.
New features in C# 7.0
The topics I will cover the following topics
- Out Variables
- Pattern Matching
- Tuples
- Local Functions
- Ref Returns & Locals
- Expression Bodied Members
- Throw Expressions
- Generalized Async Return Types
Out Variable
Previously
1 2 3 4 5 6 7 |
DateTime dt; if (DateTime.TryParse("01/01/2010", out dt)) { Console.WriteLine(dt); } |
That means it was needed to declare before to put value to a variable
but now
1 2 3 4 5 6 7 |
if (DateTime.TryParse("01/01/2010", out DateTime dt2)) { Console.WriteLine(dt2); } Console.WriteLine($"Out of the scope {dt2}"); |
If you notice the last line which is out of scope of the if statement, it also not just available in the scope of if but also outside the scope.
Another interesting thing is that if we use the var instead of DateTime then it also works like following
1 2 3 4 5 6 7 |
if (DateTime.TryParse("01/01/2010", out var dt3)) { Console.WriteLine(dt3); } Console.WriteLine($"Out of the scope 2nd Time {dt3}"); |
But what if it fails?
1 2 3 4 5 6 7 |
if (DateTime.TryParse("<strong>01/01asas/201x0</strong>", out var dt3)) { Console.WriteLine(dt3); } Console.WriteLine($"Out of the scope 2nd Time {dt3}"); |
it is obvious that the if block will fail but that new variable has been used outside the scope as well.
The answer is it will show the default value as like following output. And it will be “null” if it a reference type.
1 2 3 4 5 6 |
01-Jan-10 12:00:00 AM 01-Jan-10 12:00:00 AM Out of the scope 01-Jan-10 12:00:00 AM Out of the scope 2nd Time 01-Jan-01 12:00:00 AM |
Pattern Matching
C# is not enriched with pattern matching that much. That is the kind of thing with F# but let us explore what we have in C#
Lets’s consider the following classes:
1 2 3 4 5 6 7 8 9 10 |
public class Shape {} public class Circle: Shape {} public class Rectangle : Shape { public int Height, Width; } |
And if a method is called with “shape” reference as parameter like following, we have following option to identify the type of object
1 2 3 4 5 6 7 8 9 |
public static void IdentifyShape(Shape shape) { if (shape is Rectangle) { Rectangle rt = (Rectangle)shape; } } |
or can use following way
1 2 3 4 5 6 7 |
var rt = shape as Rectangle; if (rt != null) { //Do Something } |
But that can be easily written in following way
1 2 3 4 |
if (shape is Rectangle rt) { } |
A new way of casting things
Using the switch we are also about to find out the object type and can consider the specific check point at the same time like following
1 2 3 4 5 6 7 8 9 10 11 |
switch (shape) { case Circle c: //Do Something break; case Rectangle square when square.Height == square.Width: //Do Something break; } |
Tuples
Tuples have already existed in C# base class library as a purely library solution without any kind of change to C# Syntax.
Lets see a old way of using a Tuple
1 2 3 4 5 6 7 8 9 10 11 12 |
public static Tuple<double, double> SumAndAveragePrice(double price1, double price2) { return Tuple.Create(price1 + price2, (price1 + price2) / 2); } static void Main(string[] args) { var tuple= SumAndAveragePrice(10,20); Console.WriteLine($"SUM= {tuple.Item1} AVERAGE= {tuple.Item2}"); Console.ReadLine(); } |
The old way of Tuple only consider a predefined variable to get the returned value only as Item1, Item2…….. etc. But it was difficult to identify the type in a enterprise level application
So the new way of doing it is as below
1 2 3 4 5 6 7 8 9 10 11 12 |
static (double sum, double avg) NewSumAndAveragePrice(double price1, double price2) { return (price1 + price2, (price1 + price2) / 2); } static void Main(string[] args) { var (sum, avg) = NewSumAndAveragePrice(10,20); Console.WriteLine($"SUM= {sum} AVERAGE= {avg}"); Console.ReadLine(); } |
But it is also possible to use this in following way which is like exactly the old way with little bit of syntax changed if some one is comfortable with but not recommended to
1 2 3 4 5 6 7 8 9 10 11 12 |
static (double, double) NewSumAndAveragePrice(double price1, double price2) { return (price1 + price2, (price1 + price2) / 2); } static void Main(string[] args) { var tuple= NewSumAndAveragePrice(10,20); Console.WriteLine($"SUM= {tuple.Item1} AVERAGE= {tuple.Item2}"); Console.ReadLine(); } |
And if we use a Func, we can do like the following
1 2 3 4 5 |
var newSumAndAveragePrice = new Func<double, double, (double sum, double avg)>((x, y) => (x + y, (x + y)/2)); var (sum, avg) = newSumAndAveragePrice(5, 7); Console.WriteLine($"SUM= {sum} AVG= {avg}"); |
Output will be as excepted.
Deconstruction or multi variable declarations
It is not only for the tuple but also multi variable can be fetched using deconstruction method
say we have a following class
1 2 3 4 5 6 7 |
public class Human { public string Name; public int Age; } |
now if we want to get the properties of “Human” class like the way we are getting value from Tuple? Here is a way
1 2 3 |
var (Name, Age) = new Human(); |
But the above line would not make any sense because there is a need of “deconstruct” to make this thing work like as excepted. In the class we need to have following Deconstruct method.
1 2 3 4 5 6 7 |
public void Deconstruct(out string Name, out int Age) { Name = this.Name; Age = this.Age; } |
So the total class will be
1 2 3 4 5 6 7 8 9 10 11 12 |
public class Human { public string Name; public int Age; public void Deconstruct(out string Name, out int Age) { Name = this.Name; Age = this.Age; } } |
And in the main method simply we can use it like the following
1 2 3 4 5 |
Human human = new Human { Name = "MyName", Age = 100 }; var (Name, Age) = human; Console.WriteLine($"Name= {Name} Age= {Age}"); |
And if we do not need some variable we simply can use the “_” to skip getting the value like the following
1 2 3 |
var (Name, _) = human; |
So the age will not be available to output and other will be okay as it is.
Local Functions
It already existed previously. So let’s see how local functions were working before C#7
In simpler a local function can be used from inside a local function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class Human { public string Name; public int Age; public int AgeMulti; public int GetAge() { int AgeMultiflier() => Age * 2; AgeMulti = AgeMultiflier(); return Age; } } |
Ref Returns & Locals
Another new feature is referencing to local variable.
Say we have an array of a int
1 2 3 |
int[] myInt = { 10, 20, 30, 40 }; |
and the following line of code
1 2 3 |
ref var myVar = ref myInt[2]; |
assign the 3rd value of “myInt” to “myVar”.
changing the value of myVar will change the 3rd value of “myInt”.
1 2 3 4 5 6 |
int[] myInt = { 10, 20, 30, 40 }; ref var myVar = ref myInt[2]; myVar = 100; Console.WriteLine(string.Join(',', myInt)); |
And here is the output
1 2 3 |
10,20,100,40 |
but this will not work for the “List” though
A small hack
What will happen if we downsize the array after assigning the value to “myVar”?
1 2 3 4 5 |
Array.Resize(ref myInt, 1); Console.WriteLine(string.Join(',', myInt)); Console.WriteLine(myVar); |
It will still keep the same value even after the array is downsized.
If we see the following code
1 2 3 4 5 6 |
static ref int GetValueofAnArrayByPosiition(int[] array, int position) { return ref array[position]; } |
This function return the value of an array from a specific position. and in the main method
we can get the value by the following code
1 2 3 4 5 6 |
int[] myInt = { 10, 20, 30, 40 }; ref var myValue = ref GetValueofAnArrayByPosiition(myInt, 1); Console.WriteLine(myValue); Console.WriteLine(String.Join(',',myInt)); |
It will show following output
1 2 3 4 |
20 10,20,30,40 |
if we just write following
1 2 3 |
GetValueofAnArrayByPosiition(myInt, 1) = 100; |
then run removing the “myValue”, it will show the following output
1 2 3 |
10,100,30,40 |