New Features in C# 7

New Features in C# 7

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#.

1) Out variables

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);
	}
}

2) Type pattern matching

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);
		}
	}
}

3) Conditional switch statement

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));
		}
	}
}

4) Tuple types and tuple literals

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" />

5) Local functions

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);
}

6) Literal improvements

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;
}

7) Throw expressions

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();
}

8) Return by reference

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
}

Leave a Comment

Your email address will not be published. Required fields are marked *