Srikanth Technologies

What's New In C# 7.0

The following are the new features of C# 7.0 that was made available in March, 2017.

Declaration of out variables

Starting from C# 7.0, it is possible to declare out variables right at the time of using them. Variables declared at the time of calling function, are given scope of enclosing block so that they can be used anywhere in the enclosing block, as shown in the following example.

It is also possible to declare out variables using var instead of specific data type as compiler can generally infer the data type based on the function being called (unless there are some conflicting overloads)

 class OutEnhancements 
 {
        public static void Main()
        {
            Fun(out int v);        // declare variable v and pass it to Fun() as out parameter
            Console.WriteLine(v);  // Variable v can be used in enclosing block.

            Fun(out var v2);
            Console.WriteLine(v2);
        }
        public static void  Fun(out int value)
        {
            value = 100;
        }
 }

Returning more than one value using Tuple

A function can return only one value. If a function has to return more than one value then we use pass by reference to send more than one value back to caller (using ref or out parameters).

Another possibility is to return a struct or class that contains multiple values to be returned from function.

C# 7.0 provides a new feature using which a function can return multiple values in the form of Tuple.

In the following code we return name and email from GetInfo() function as a Tuple. As Tuple members are accessed using Item1, Item2 etc., we access mobile number using Item1 and email using Item2.

 class Tuples
 {
        public static void Main()
        {
            var info = GetInfo();
            Console.WriteLine($"{info.Item1} , { info.Item2}");

            var details = GetDetails();
            Console.WriteLine($"{details.mobile}, {details.email}"); // Accessing members using names provided by function 
        }
        // Return a Tuple of two values
        public static (string,string) GetInfo()
        {
            return ("9059057000", "srikanthpragada@gmail.com");
        }
        // Return values with names
        public static (string mobile, string email) GetDetails()
        {
            return ("9059057000", "srikanthpragada@gmail.com");
        }
}
    

Note: In order to use this features, first we need to get System.ValueType package from NuGet using NuGet Package Manager or Package Manager Console.

It is possible to specify names for return values so that values are referred by those names, as shown with function GetDetails().

Deconstruction

Deconstruction allows Tuple returned by a function to be split into multiple values and copy them into new variables.

We can either provide data type to these variables or declare them using var.

class MyTime
{
        public int Hours { get; set; } = DateTime.Now.Hour;
        public int Mins { get; set; } = DateTime.Now.Minute;
        public int Secs { get; set; } = DateTime.Now.Second;

        public (int , int , int ) GetTime()
        {
            return (Hours, Mins, Secs);
        }
        public static MyTime Now
        {
            get => new MyTime();
        }

        // Deconstruct method to copy data to out parameters
        public void Deconstruct(out int h, out int m, out int s)
        {
            h = Hours; m = Mins; s = Secs;
        }
}
class Deconstruction
{
        public static void Main()
        {
            MyTime t1 = new MyTime();

            // Deconstruction of Tuple into three variables
            var (hour, min, sec) = t1.GetTime();
            Console.WriteLine($"{hour}:{min}:{sec}");

            // Deconstruction of MyTime into variable declared using var individually
            (var hr, var mi, var se) = MyTime.Now;
       
            var (h, m, s) = MyTime.Now;   // Another example for deconstruction 
            Console.WriteLine($"{h}:{m}:{s}");
        }
}
Deconstruction is possible for any type as long as type provides a function called Deconstruct(), which copies values of object into given out parameters.

In class MyTime, we have Deconstruct() function that copies values of properties to out variables and it can be used whenever we want to deconstruct an object of MyTime to variables.

Local Functions

It is possible to create a function inside another function. Inner function is called as local function. The local function is callable only from enclosing function.

Local function has access to local variables and parameters of enclosing function.

class LocalFunctions
{       
        public static void Main()
        {
            Console.WriteLine(NextEven(10));

            // Local Function - can be used only in Main()
            int NextEven(int v)
            {
                return v % 2 == 0 ? v + 2 : v + 1;
            }          
        }
}

Enhanced Literals

Numeric literals can have _ (underscore) to separate digits to increase clarity of number.

Another addition to literals is binary literal, where you can write a number in binary using 0b as prefix.

int num = 10_4_232;
int binary = 0b1111;

Console.WriteLine(num);
Console.WriteLine(binary);
     

More Expression Bodied Functions

C# 7.0 supported expression bodied function syntax in getter functions, constructors and finalizers.

C# 6.0 introduced this feature for normal functions and C# 7.0 enhanced it further.

 class Stack
 {
        private int top;

        public Stack() => top = -1;  // Expression body in constructor 
        public int Length {
            get => top + 1;  // Expression body in getter method of property
        } 
 }

Returning ref

C# 7.0 allows a function to return a reference to value using ref keyword. It allows return value of the function (a reference) to be used to access a variable or array element and even change it.

class RefReturns
{
        public static void Main()
        {
            int[] a = { 10, 20, 30, 40, 50 };

            ref int refto20 = ref Find(a, 1);  // get a reference to element at index 1 with value 20

            Console.WriteLine(refto20);
            refto20 = 25;   // modify value at index 1 pointed by reference 
            Console.WriteLine(a[1]);
        }

        // Function returns a Reference and not Value
        public static ref int  Find(int [] values, int pos)
        {
            return ref values[pos]; // return a reference 
        }
}

Throw in Expression

In order to throw an exception in C# 6.0 or before, we use throw statement. Starting from C# 7.0, we can throw an exception as part of an expression also.

class ThrowExceptionFromExpression
{
        public static void Main()
        {
            int v = 10;

            Process(v);
        }
        public static int Process(int v)
        {
            // Throw an Exception from an expression 
            return v > 10 ? v + 1 : throw new ArgumentException("Invalid Number!");
        }
}

Pattern Matching

A couple of new syntaxes allow programmer to check the type of a variable and extract value when variable contains value of specified type.

The following are different forms of pattern matching possible in C# 7.0:

Is-Expressions

We know we can use is operator to test whether an object reference is of a specific type.

In addition to that C# 7.0 added type patterns using which we can extract value from an object into a specific type of variable, only when object contains the value of the mentioned type.

 object v = 10;

      Console.WriteLine(v is int);  // True if v contains int 
      // Take value from v into i only when v is of type int 
      if (v is int i)
            Console.WriteLine(i);
     else
            Console.WriteLine("Not an int");
 

Patterns in switch

The following are additions to switch statement, which include pattern matching.

The following code shows how to use patterns and extract value into variable in switch. It also shows how to use additional condition in switch case using when.

object v = "Srikanth";
switch(v)
{
         case String s when (s.Length > 5): // Extract value and test its value with condition 
                    Console.WriteLine($"Large String {s}");
                    break;
         case String s:  // if it is a string then extract value into s
                    Console.WriteLine($"String {s}"); 
                    break;
         case DateTime d:   
                    Console.WriteLine($"Date {d}");
                    break;
         case int n:  
                    Console.WriteLine($"Integer  {n}"); 
                    break;
}