7 Features of C# 7.0 Worth Knowing
As you are aware of C# 7.0 and Visual Studio 2017 have been released. In this
article I quickly take you through seven new features of the language that are
worth knowing. I assume that you are familiar with C# 6.0 and hence I am not
going into too much technical details of these features here. My aim is to
quickly let you know of a few interesting features that can make your C# code
more efficient and clear. Let's get going.
1. Tuple types and Tuple literals
At times you need to return more than one values from a method. Developers
used different techniques to achieve that. Tuples was one of them. In C# 7.0
returning a tuple from a method is easy as shown below :
private (string s1,string s2,string s3) GetLanguages()
{
return ("C#","VB.NET","F#");
}
The GetLanguages() method returns a tuple literal that
contains three values. Notice how the method return type has been specified
using the new tuple type syntax.
The tuple thus returned can be accessed like this :
var retVal = GetLanguages();
MessageBox.Show($"{retVal.s1} -
{retVal.s2} - {retVal.s3}");
Note that to work with this example you may need to add System.ValueTuple
NuGet package.
2. Local functions
At times you need to execute a bunch of statements or logic multiple times.
Ordinarily you would create a private helper method and call that method as and
when needed. But what if that logic is needed multiple times only in a single
method? That's where local functions can be useful.
As the name suggests a local function is a function that exists locally to
another function. It's like a function inside a function. Let's create one and
understand the how they work:
public void DoWork()
{
string GetRandomCode()
{
return "ABC" + new Random().Next();
}
string code1 = GetRandomCode();
string code2 = GetRandomCode();
string code3 = GetRandomCode();
MessageBox.Show($"{code1} - {code2} - {code3}");
}
The DoWork() is the main function doing some lengthy work. Inside, we create
GetRandomCode() function. This is a local function and here simply generates a
random string code. GetRandomCode() is called multiples times by the other
parts of DoWork(). Obviously, GetRandomCode() won't be accessible to withinn the
other methods.
3. Enhancements to is-expressions and switch statement
C# 7.0 introduces what is known as pattern matching - a feature that allows
you to test a value for certain shape and also extract information form
that value.
Currently pattern matching is being introduced in two ways but in the future
more might get added. The two constructs that got enhanced with pattern matching
are is-expressions and switch statement. Consider the following code :
private void ShowType(object param)
{
string msg = "";
if(param is null)
{
msg = "Value is null.";
}
if (param is int a)
{
msg = $"Value is integer - {a}";
}
if(param is string s)
{
msg = $"Value is string - {s}";
}
MessageBox.Show(msg);
}
The ShowType() method accepts an object parameter. Inside, three if blocks
test for certain pattern and assign the msg variable accordingly.
- The first if statement used constant pattern that checks the
value of the parameter against constant - null.
- The second if statement uses type pattern and tests whether the
parameter is an integer. Further, it puts the value of the parameter into a
if it is indeed an integer.
- The third if statement works quite similar to the second one but tests
against string data type.
You can use the pattern variables (a and s in the above
example) inside the respective blocks to retrieve their values.
Now consider a switch statement that uses patterns :
private void ShowData(object param)
{
string msg = "";
switch(param)
{
case Employee e:
msg = $"Employee - {e.FirstName}
{e.LastName}";
break;
case Customer c:
msg = $"Customer - {c.CustomerID}
{c.CompanyName}";
break;
case Order o:
msg = $"Order - #{o.OrderID}
{o.CustomerID} {o.OrderDate}";
break;
case null:
msg = $"Data is null.";
break;
default:
msg = "Unknown data.";
break;
}
MessageBox.Show(msg);
}
As is clear from the switch statement, you can now use any types in the
statement. The individual case clauses uses patterns to test the param against
certain type or a constant (Employee, Customer, Order, null etc.). You can
access the pattern variables inside the individual case blocks as before.
Remember that with this arrangement the order in which the case clauses appear
matters. If some cause meets the pattern further case clauses won't be
evaluated.
4. Deconstruction
While constructing an object you typically pass values to it and then assign
those values to the internal members of the class. Deconstruction does exactly
opposite. It retrieves values from an object and assigns them to variables.
Let's understand this with an example.
Consider the following Employee class :
class Employee
{
public int EmployeeID { get; set;}
public string FirstName { get; set; }
public string LastName { get; set; }
public Employee()
{
}
public Employee(int id,string fname,
string lname)
{
this.EmployeeID = id;
this.FirstName = fname;
this.LastName = lname;
}
public void Deconstruct(out int id,
out string fname,out string lname)
{
id = this.EmployeeID;
fname = this.FirstName;
lname = this.LastName;
}
}
The Employee class has three public properties. One of the two constructors
accept values that are then assigned to these three properties. The
Deconstruct() method is more important to us. This is the method that does the
deconstruction and its name must be Deconstruct().
The Deconstruct() method accepts certain output parameters. These parameters
depend on the values that you wish to deconstruct. Inside, it assigns the
corresponding property values to these output parameters.
To see the deconstruction in action you would write :
private Employee GetEmployee()
{
Employee emp = new Employee(1, "Nancy", "Davolio");
return emp;
}
(int id, string fname, string lname) = GetEmployee();
MessageBox.Show($"{id} - {fname} - {lname}");
The GetEmployee() method simply creates a new Employee object and returns it
to the caller. Notice how the calling code deconstructs the Employee object.
This is when the Deconstruct() method that you wrote earlier gets called. You
can use id, fname and lname variables as usual.
5. Out variables
In the earlier versions of C# using output parameters required that you
declared the necessary variables first and then pass them to a method using out
keyword. In C# 7.0 you can simplify your code by doing both of these tasks at
one go. This feature is called out variables. Suppose you have
a simple method that takes two output parameters :
private void SetOutVariables(out string a,out string b)
{
a = "Hello";
b = "World!";
}
Inside, the SetOutVariables() method simply assigns certain string values to
a and b. To call SetOutVariables() you would do the following :
SetOutVariables(out string s1, out string s2);
MessageBox.Show(s1 + " " + s2);
As you can see, the s1 and s2 variables are declared and passed while calling
the method itself. You can then access s1 and s2 just like any other variables.
If you are not interested in all the output parameter values you can skip some
using the following syntax :
SetOutVariables(out string s1, out _);
MessageBox.Show(s1);
Using the underscore character you can ignore an output value.
6. Digital separators and binary literals
A digital separator allows you to separate digits of any number for the sake
of better readability. Consider the following code :
var i = 123456789;
var j = 123_456_789;
The first line is an ordinary variable declaration. The second line uses
digital separator (the underscore character) to separate digits of a number.
This separation is purely for the sake of readability.
In the older versions of C# numbers could be represented as decimal or
hexadecimal values. In C# 7.0 you can also write numbers in binary form. See the
following code :
int i = 0b101000;
MessageBox.Show(i.ToString());
The above code stores number 40 in an integer variable using its binary
representation 101000. The prefix of 0b or 0B indicate that its a binary
literal. Of course, when you output the value of i it's displayed as 40 and not
in it binary format.
7. Throw expressions
In C# 7.0 you can throw exceptions directly from expressions. The following
example will show you how :
string s = (DateTime.Now.Day > 20 ? "Month end" :
throw new Exception("Too early!"));
The above statement directly throws an exception if month day is less than
20.
There is more - ref returns and locals, enhancements to expression bodied
members and a few others. You may read more details
here.
That's it for now ! Keep coding !!