Friday, July 7, 2017

New Features in C# 7

Deconstructors
    Usually Constructor would do is, it will create a new object of given type with given parameters. So what the Deconstructor would do is, it will deconstruct the object back into it’s original parts. To be specific, we have the control of specifying how would you like the object to be deconstructed.

The method must be named Deconstruct and have a return type of void. The parameters to be assigned all must be out parameters, and because they are out parameters with a return type of void, C# allows the Deconstruct method to be overloaded just based on these parameters.


public class Employee
{
    public string FirstName { get; }
    public string LastName { get; }

    public Employee(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = LastName;
    }
}

We'll see how we can add a Deconstructor to Employee.

public class Employee
{
    public string FirstName { get; }
    public string LastName { get; }

    public Employee(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = LastName;
    }

    public void Deconstruct(out string firstName, out string lastName)
    {
        firstName = FirstName;
        lastName = LastName;
    }
}

We'll see how to call the Deconstruct.

Employee employee = new Employee("AAA", "BBB");

1.  // Example 1: Deconstructing declaration and assignment.
    (string firstName , string lastName) = employee;

2.  // Example 2: Deconstructing assignment.
     string firstName , lastName = null;
    (firstName , lastName) = employee;

3.  // Example 3: Deconstructing declaration and assignment with var.
  var (firstName, lastName) = employee;

The following example shows a Employeeclass with a deconstructor that returns the FirstName and LastName properties:
public class Employee
{
    public string FirstName { get; }
    public string LastName { get; }
    public int Age { get; set; }
    public string Email { get; set; }

    public Employee(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = LastName;
    }
    public void Deconstruct(out string firstName, out string lastName)
    {
        firstName = FirstName;
        lastName = LastName;
    }
}

var emp= new Employee
{
    FirstName = "FN",
    LastName = "LN",
    Email = "test@example.com",
    Age = 99
};

(var firstName, var lastName) = user;

We are creating a Employee object, and then deconstructing it into the firstName and lastName variables, which are declared as part of the deconstruction.

    Here we can notice C# is allowing simultaneous assignment to multiple variables of different values. This is not the same as the null assigning declaration in which all variables are initialized to the same value (null):
     string firstName , lastName = null;


The C# 7.0 tuple-like syntax requires that at least two variables appear within the parentheses. For example, (FileInfo path) = pathInfo; is not allowed even if a deconstructor exists for:

public void Deconstruct(out FileInfo file)

With the new tuple-like syntax, each variable is assigned a different value corresponding not to its name, but to the order in which it appears in the declaration and the deconstruct statement.

In other words, we can’t use the C# 7.0 deconstructor syntax for Deconstruct methods with only one out parameter.

Deconstruct() has the ability to split a tuple’s values up and put them into variables. In the above expamples, we can see how you take a tuple with a string and int and deconstruct them into variables.

Deconstruct also does not need to be directly attached to the class. C# allows the method to be implemented as an extension method as well.

public class AbstractClassA
{
  public string UserID{ get; }
   public string IPAddress{ get; }
}
static class AbstractExtensions
{
  public static void Deconstruct
    (this AbstractClassA obj, out string userID, out string ipAddress)
  {
    userID= obj.UserID
    ipAddress= obj.IPAddress;
  }
 
}

class Request : AbstractClassA
{
 public FirstAndLastNameNoDeconstructor(string userID, string ipAddress)
 {
   UserID = userID
   IPAddress = ipAddress;
 }
}

Request obj = new Request ("A001", "127.0.0.1")
var(userID, ipAddress) = obj;


Pattern Matching with the Switch Statement
C# 7.0 allows user to use pattern in IS statement and with SWITCH statement, so we can match pattern with any datatype, patterns can be constant patterns, Type patterns, Var patterns. following sample snippet will clear your concepts, let's start with IS pattern


public void PrintStars(object o)
{
    if (o is null) return;     // constant pattern "null"
    if (!(o is int i)) return; // type pattern "int i"
OR
  if (o is int i || (o is string s && int.TryParse(s, out i))

    WriteLine(new string('*', i));
}

Switch statements with patterns
    1. Patterns can be used in case clauses
    2. Case clauses can have additional conditions on them

switch(shape)
{
    case Circle c:
        WriteLine($"circle with radius {c.Radius}");
        break;
    case Rectangle s when (s.Length == s.Height):
        WriteLine($"{s.Length} x {s.Height} square");
        break;
    case Rectangle r:
        WriteLine($"{r.Length} x {r.Height} rectangle");
        break;
    default:
        WriteLine("<unknown shape>");
        break;
    case null:
        throw new ArgumentNullException(nameof(shape));
}

Local Functions
    Local methods and functions is already there in current version of C# (Yes, we can achieve them using Deleage, Func and Action types), but still there are some limitations to local method, we can not have following features in it
    1. Generic
    2. out parameters
    3. Ref
    4. params
Example:
bool IsPalindrome(string text)
{
  if (string.IsNullOrWhiteSpace(text)) return false;
  bool LocalIsPalindrome(string target)
  {
    target = target.Trim();  // Start by removing any surrounding whitespace.
    if (target.Length <= 1) return true;
    else
    {
      return char.ToLower(target[0]) ==
        char.ToLower(target[target.Length - 1]) &&
        LocalIsPalindrome(
          target.Substring(1, target.Length - 2));
    }
  }
  return LocalIsPalindrome(text);
}

Return by Reference
Since C# 1.0 it has been possible to pass arguments into a function by reference (ref). The result is that any change to the parameter itself will get passed back to the caller.

In C# 7, we can also use ‘ref’ for returning a variable from a method i.e. a method can return variable with reference. We can also store a local variable with reference.

public ref int GetFirstOddNumber(int[] numbers) {
    for (int i = 0; i < numbers.Length; i++) {
        if (numbers[i] % 2 == 1) {
            return ref numbers[i];
        }
    }
    throw new Exception("odd number not found");
}

int[] x = { 2, 4, 62, 54, 33, 55, 66, 71, 92 };
ref int oddNum = ref GetFirstOddNumber(x);
Console.WriteLine($"\t\t\t\t\t{oddNum}");

oddNum = 35;

for(int i=0; i < x.length; i++)
{
    Console.WriteLine($"{x[i]}\t");
}

Th output:
                               33
2     4     62     54    35     55     66     71     92


The GetFirstOddNumber(), retruns 33 value and it's holding by oddNum and after we have value, we modified the value and it is reflecting to the main collection.

 When declaring a reference local variable, initialization is required. This involves assigning it a ref return from a function or a reference to a variable:

     ref string text;  // Error

We can’t declare a by reference type for an auto-implemented property:
     class Thing { ref string Text { get;set; }  /* Error */ }

Properties that return a reference are allowed:
     class Thing { string _Text = "Inigo Montoya";
          ref string Text { get { return ref _Text; } } }

A reference local variable can’t be initialized with a value (such as null or a constant). It must be assigned from a by reference returning member or a local variable/field:
     ref int number = null; ref int number = 42;  // ERROR


Out Variables
If you had to pass a variable as out parameter, then the variable must had been declared before passing it to the method. But in C# 7, you can declare variable directly when you are passing it inside a method.



Throw Expressions

In C# 7, we can throw an exception directly through expression. Thus, an exception can be thrown from an expression.
static void Main(string[] args)
{
var a = Divide(10,0);
}
public static double Divide(int x, int y)
{
return y!=0? x %y : throw new DivideByZeroException();
}

Record Type
C# support record type, which is nothing but a container of a properties and variables, most of the time classes are full with properties and variables, we need lot of code to just declare them but with the help of Record Type you can reduce your effort, see below snippet

class studentInfo
{
    string _strFName;
    string _strMName;
    string _strLName;
    studentInfo(string strFN, string strMN, string strLN){
        this._strFName = strFN;
        this._strMName = strMN;
        this._strLName = strLN;
    }
    public string StudentFName {get{ return this._strFName;}}
    public string StudentMName {get{ return this._strMName;}}
    public string StudentLName {get{ return this._strLName;}}
}
In above code we have a class with property, constructor and variable, so access and declare variable i need to write more code.

To avoid it i can use  Record Type in C#, see below snippet


class studentInfo(string StudentFName, string StudentMName, string StudentLName);

That's it and we have Done !


Non-'NULL' able reference type
Null reference is really a headache for all programmers, it is a million dollar exception. If you don't check them you got runtime exception or if you check them for each object then your code goes long and long, To deal with this problem C# 7.0 come with non-nullable reference types

**I think syntax for it yet not fixed still they have release following syntax

'?' is for nullable value-type and '!' is for non-nullable reference type

Hide   Copy Code
int objNullVal;     //non-nullable value type
int? objNotNullVal;    //nullable value type
string! objNotNullRef; //non-nullable reference type
string objNullRef;  //nullable reference type
Now look at the following complier effect after we run this snippet

Hide   Copy Code
MyClass objNullRef;  // Nullable reference type
MyClass! objNotNullRef; // Non-nullable reference type

objNullRef = null;   // this is nullable, so no problem in assigning
objNotNullRef = null;   // Error, as objNotNullRef is non-nullable
objNotNullRef = objNullRef;      // Error, as nullable object can not be refered

WriteLine(objNotNullRef.ToString()); // Not null so can convert to tostring
WriteLine(objNullRef.ToString()); // could be null

if (objNullRef != null) { WriteLine(objNullRef.ToString); } // No error as we have already checked it
WriteLine(objNullRef!.Length); // No error

More Expression-Bodied Members

C# 6.0 introduced expression-bodied members for functions and properties, enabling a streamlined syntax for implementing trivial methods and properties. In C# 7.0, expression-bodied implementations are added to constructors, accessors (get and set property implementations) and even finalizers (see Figure 11).

Figure 11 Using Expression-Bodied Members in Accessors and Constructors
class TemporaryFile  // Full IDisposible implementation
                     // left off for elucidation.
{
  public TemporaryFile(string fileName) =>
    File = new FileInfo(fileName);
  ~TemporaryFile() => Dispose();
  Fileinfo _File;
  public FileInfo File
  {
    get => _File;
    private set => _File = value;
  }
  void Dispose() => File?.Delete();
}