Monday, March 6, 2017

C# 7.0 features in Visual Studio 2017

Exploring C# 7.0 features in Visual Studio 2017


1 Nested Local Functions


public static void OuterFunction(bool callInner)
{
   int outerVar = 10;

   WriteLine($"From outer func {outerVar}");

   if (callInner) InnerFunction();

   void InnerFunction()
   {
      int output = outerVar * 10;

      WriteLine($"From inner func {output}");  
            
      OuterFunction(false);
   }         
}

2 Value Tuples


Tuple literals. The following example shows declaring tuple literals and using them.
var literal = (100, 200, 300);
var namedliteral = (Length: 3.12, Height: 2.34, Width: 3.14);
 
WriteLine($"Val1:{literal.Item1}, Val2:{literal.Item2},
 Val3: {literal.Item3}");
 
WriteLine($"Length:{namedliteral.Item1}, Height:{namedliteral.Item2},
 Width: {namedliteral.Item3}");
 
WriteLine($"Length:{namedliteral.Length}, Height:{namedliteral.Height}, 
Width: {namedliteral.Width}");


The tuple types (members returning multiple values) and how they can be used are shown in in the following example:
//tuple element
 
(double, double, double, double) GetProprerties(double input) 
{
   //tuple literal 
   var literal = (value1, value2, value3, value4);   
   return literal;
}
 
//Named tuple element
 
(double Length, double Width, double Height, double Weight)
  GetProprerties2(double input)
{
  return (value1, value2, value3, value4);
}
 
//Using tuple elements 
 
public void PrintProperties()
{
  var point = GetProprerties(100);
 
  WriteLine($"Value 1: {point.Item1}, Value 2: {point.Item2}," +
            $" Value 3: {point.Item3}, Value 4: {point.Item4}");
 
  var point2 = GetProprerties2(100);
 
  WriteLine($"Value 1: {point2.Length}, Value 2: {point2.Width}," +
            $" Value 3: {point2.Height}, Value 4: {point2.Weight}");
 }
The tuple type can be further deconstructed into variables as shown below:
(double l, double w, double h, double wt) = GetProprerties2(100);
 
WriteLine($"Length is {l}");


3 Pattern Matching Extensions


Pattern Matching in 'is-expression' 

The extensions to is-expressions (using various types of patterns) are shown in following statements: 
//constant pattern
 
if (o is Country) WriteLine("object is a country");
 
//type pattern
 
if (o is Country c && c.IsIsland)
{
    WriteLine($"object is a country and an island"); 
}
if (o is Country c2)
{
    WriteLine($"object is a country with {c2.States.Count} states"); 
}
 
//var pattern
 
if (o is var x)
{
    WriteLine($"object is of type {x.GetType()}"); 
}


Pattern Matching in 'switch' statement 

A welcome feature here is that any type can be used in the switch statement in C# 7.0. An example of switch statement with pattern matching is shown below (a few support types are declared initially): 
class GeoEntity { }
 
class Country : GeoEntity{
   publicbool IsIsland { get; set; } = false;
   public List<State> States { get; set; } = new List<State>();
}
 
class State : GeoEntity {public List<Town> MajorTowns { get; set; }}
 
class Town : GeoEntity { publicstring Name { get; set; } }
 
static void PrintDetailsGeo(GeoEntity entity)
{
  switch (entity)
  {
     case Country c when c.IsIsland == true:
     WriteLine("Island Country"); c.IsIsland = false; 
     break;
     case Country c:
     WriteLine($"Country with {c.States.Count} states");
     break;
     case State s:
     WriteLine($"Country with {s.MajorTowns.Count} states");
     break;
     case Town t:
     WriteLine($"Town called {t.Name}");
     break;
     default:
     WriteLine("None"); 
     break;
     casenull:
     WriteLine("Null"); 
     break;      
   }
}

4 Returning Values by Reference (Kind of Pointers!!)


C#7.0 allows for returning and storing variables by reference, similar to passing variables by reference. Returning value by reference is particularly useful for avoiding copying of resources in memory intensive applications.  The following example declares a type called Person and shows how to obtain the reference of a object of type Person in an array of Person type.
 class Person
 {
    public int ID { get; set; }
    public int Age { get; set; }
    public string Name { get; set; }
    public override string ToString(){return$"{ID},{Age},{Name}";}
 }
 
 public static ref Person FindPerson(Person P, Person[] PList)
 {
    for (int i = 0; i < PList.Length; i++)
         if (PList[i].ID == P.ID)
              return ref PList[i];
 
    throw new IndexOutOfRangeException($"{nameof(P)} not found");
 }
How to use the reference to modify a specific person?
 //Calling the function and obtaining the location of specific object
 
 Person PofInterst = new Person() { ID = 2, Age = 11, Name = "BBB" };
 
 ref Person location = ref FindPerson(PofInterst, Plist);
 
//Changing the object using the location.
 
location = new Person() { ID = 4, Age = 14, Name = "DDD" };
A ref local (variable called location in example above) is initialized to a storage location as shown above by calling the FindPerson function. Note that this ref local cannot be changed to point to other location. In other words, location is an immutable pointer to Person object with initial ID 2. Also note that reference can only returned for variables that are deemed safe (location maintained even after the function is exited). Hence the ref function cannot be used for local variables or non-ref parameters that would be garbage collected.

5 Other Features

There is no need of declaration of out variable before the point of usage. However, the scoping is somewhat strange. In the example below, the scope of variable 'i' is expected to be limited to the if-block. It is not the case in current RC version.
static void Parse ToDouble(object val)
{
   if (val is string s && double.TryParse(s, out var i))
   {
      WriteLine($"Parsed value: {2*i}"); }
   }
}

Throw expressions can be placed directly in an expression as shown in following examples:
Country x = c ?? throw new Exception("Null reference");
 
public string SomeFunction()=> throw new NotImplementedException();


Expression bodied members introduced in C#6.0 are now allowed on accessors, constructors and finalizers. This is shown in a very crude example below (the example is for demo and is not a robust one..):
 class Employee {
    string name;
 
    //constructor 
 
   public Employee(string firstname, string lastname)
      => name = firstname + ";" + lastname;    
 
    public string FirstName
    {
       get => name.Split(new char[] { ';' }, 
         StringSplitOptions.RemoveEmptyEntries)[0];
 
       set => name = name.Replace(FirstName, value);
    }
 }

Literal Improvements include introduction of binary literals and digit separators. Binary literals start with 0b. The digit separator '_' can be placed anywhere in a number literal without influencing its value. In case of decimal literals, the sperator should not be placed adjacent to decimal character or sign character.
var binnum = 0b1110_1011_0111_1001_1010_0010_101;
 
var dnum = -1_234.35_6;



No comments:

Post a Comment

Encrypt/Decrypt the App.Config

Program.cs using System; using System.Diagnostics; using System.IO; namespace EncryptAppConfig {     internal class Program     {         pr...