I have been wanted to write about c# 7 for long time but I have been so overwhelmed with work lately.
So now that I have some time at my disposal lets review all of the latest and greatest from c# 7. As probably most of you know, in this release there are not that many dramatic changes unlike some of the previous versions of c#.
In the past any time you had to use a variable with a method that uses out variables, it was a cumbersome process with multiple steps. Now we have out variables that come to the rescue.
Here is an example:
//Using Out variables private void outVariablesTest() { if (int.TryParse("32132", out int result)) { WriteLine(result); } }
In the past casting was kind of annoying, because you had to check if a variable could be casted to a particular type and if so then you do the actual casting and assignment. Now there is a really nice and elegant way to do this. In the following example, we have list of shapes and we want to get only the triangles and put them into triangles list:
//type pattern matching private void constantPatternTest() { var shapes = new List{new Rect(), new Circle(), new Triangle(), new Rect(), new Triangle()}; var triangles = new List (); foreach (var shape in shapes) { if (shape is Triangle triangle) { triangles.Add(triangle); } } }
The best way to understand conditional switch statement is by example. Lets say we have a list of shapes and we want to get only the triangles, rectangles and circles, but only those that have area larger than 0. Now there is a very nifty way to do something like this:
//Conditional switch statement private void switchStatements() { var shapes = new List{new Rect(), new Rect(22), new Triangle(1), new Triangle(15), new Rect(3)}; var triangles = new List (); var rects = new List (); var circles = new List (); foreach (var shape in shapes) { switch (shape) { case Rect rect when (rect.Area > 0): rects.Add(rect); break; case Triangle triangle when (triangle.Area > 0): triangles.Add(triangle); break; case Circle circle when (circle.Area > 0): circles.Add(circle); break; default: WriteLine("Skipping this shape .."); break; case null: throw new ArgumentNullException(nameof(shape)); } } }
In my opinion tuples are the biggest improvement in this version of c# 7. In the past working with tuples was cumbersome and kind of hard to read, the alternative of using out parameters was even worse. A common way of solving related issues was making a new model that holds the data and it is used only one time, which always seemed like a overkill to me. Thanks to c# 7 now we have a very beautiful way of solving issues like this. In the following example we have list of shapes and we want a method that will filter out and give us back list of circles, list of rectangles and list of triangles:
//Tuple types and tuple literals private void consumeTuples() { var shapes = new List{new Rect(), new Triangle(22), new Circle(1), new Triangle(15), new Rect(3)}; // deconstructing assignment var (rects, triangles, circles) = GetGroupedShapes(shapes); WriteLine($"There are {rects.Count} rectangles."); WriteLine($"There are {triangles.Count} triangles."); WriteLine($"There are {circles.Count} circles."); } private (List , List , List ) GetGroupedShapes(List shapes) { var rects = shapes.OfType ().ToList(); var triangles = shapes.OfType ().ToList(); var circles = shapes.OfType ().ToList(); return (rects, triangles, circles); }
The only caveat to this is that if you are using .NET 4.6.2 or lower, and .NET Core 1.x, you need to install the NuGet package System.ValueTuple or use package reference in VS 2017
<PackageReference Include="System.ValueTuple" Version="4.3.1" />
Have you ever had an issue where you have a method, which needs another method, but you don’t want the second method to be used by anything else ever, because it is so closely connected and related only to the first method. Typically that requires a lot of comments and explanations why that is the way it is. However in c#7 there is a very elegant way to do this with local functions. In the following example we want to get factorial of a number and add it to the factorial of another number and return the result:
private long getSumFromFactorialsOfNumbers(long value1, long value2) { long GetFactorial(long number) { if (number == 0) { return 1; } return number * GetFactorial(number-1); } return GetFactorial(value1) + GetFactorial(value2); }
Working with numbers is really vital for any programming language, however sometimes when working with bigger 6+ digit numbers, it might get challenging to read the numbers, I am talking specifically for readability related issues. A nifty new feature in C# 7.0 is the ability to use the symbol _ (underscore) to occur as a digit separator inside numbers like this:
private int GetNumber()
{
return 286_351_235;
}
Even though it doesn’t seem like a major change in the language, now we have the concept of throwing exceptions as expressions. For example if we have a Person class and if the constructor expects name, but we also require the name to not null, and if it happen to be null, we want to throw exception:
public class Person { public string Name { get; set; } public Person(string name) => Name = name ?? throw new ArgumentNullException(); }
Another cool thing added to c# 7 is ref returns. I don’t see this feature being overly utilized, but it is still a nice to have. The idea is to be able to return simple types like integers, strings or bools by reference, for example:
public ref int FindNumber(int searchNumber, int[] numbers) { for (int i = 0; i < numbers.Length; i++) { if (numbers[i] == searchNumber) { return ref numbers[i]; // return the storage location, not the value } } throw new IndexOutOfRangeException($"{nameof(searchNumber)} not found"); } void Init() { int[] array = { 4, 68, -159, 5, 1888, 35, -61 }; // aliases 4's place in the array ref int place = ref FindNumber(5, array); // replaces 5 with 10 in the array place = 10; Console.WriteLine(array[3]); // prints 10 }
Recent Comments