神刀安全网

New features of C# 7.0

Preface

It seems like only yesterday we got C# 6, it all happens in the software development land. And now you are seeing C#7.0. Jeez, it is having more cool features than C#6.0 and damn sure after going through these you will also be on my side waiting for that one fine day.

For Trying C# 7.0 you need to do the following

  • Visual Studio 15 preview
  • Set __DEMO__ and__DEMO_EXPERIMENTAL__ as Conditional compilation symbol in project settings.

Feature List in C# 7.0

  1. Local functions – code available currently in github
  2. Tuple Types and literals
  3. Record Types
  4. Pattern matching
  5. Non Nullable reference types
  6. Immutable types

Features

Local Functions

Upto C# 6.0

Ability to declare methods and types in block scope as like variables. It is already possible in current version of C# using Func and Action types with anonymous methods, but they lack these features.

  • Generics
  • Ref and out
  • Params

We can’t utilize these three features while using Func and Action.

In C# 7.0

Local functions would have the same capabilities as normal methods but they can be only accessed within the block they were declared in.

public int Foo(int someInput)   {   int Bar()   {   Console.WriteLine(“inner function”);   }   return Bar();   }   

Advantages over lambda expressions

  • The syntax is similar and more consistent with the methods.
  • Recursion and forward references would work for the local functions but not for the lambda.
  • The lambda causes one or two memory allocations (the frame object for holding the variables inside that function and a delegate); but local function causes no memory allocations.
  • A local function can be generic.
  • It can accept ref and out parameters.
  • The direct method invocation is indeed faster than a delegate invocation.
Tuple Types and literals

Multiple return types – up to C# 6.0

In our current version of C# for returning multiple values from a method we have follow on of these methods.

  1. Out parameters
  2. Tuple-Types
  3. Class/ Struct

The most common reason for grouping up of temporary variables is to return multiple values from a method.

Out parameters

public void GetMultipleValues(string deptName, out double topGrossSalary, out string hrName) { ... }      double gross,    string hrName;   GetMultipleValues (name, out gross, out hrName);   Console.WriteLine($"Gross: { gross }, Hr: { hrName }");    

The disadvantage of using out parameter is we can’t use it for async methods. And you have declare parameters upfront and you have specify the specific type.

Tuple-Types

Currently C# has tuple type in order to hold multiple non related values together. We can rewrite the same method to use tuples to achieve the same functionality.

public Tuple<double, string> GetMultipleValues(string name) { ... }      var tupleSalary = GetMultipleValues (name);   Console.WriteLine($"Gross: { tupleSalary.Item1 }, Hr: { tupleSalary.Item2 }");    

This does not have the disadvantages of out-parameters, but the resulting code is rather obscure with the resulting tuple having property names like Item1 and Item2.

Class / struct

You could also declare a new type and use that as the return type.

Struct TopSalaryAndHr { public double topGrossSalary; public string hrName;}   public TopSalaryAndHr GetMultipleValues(string name) { ... }      var tupleSalary = GetMultipleValues (name);   Console.WriteLine($"Gross: { tupleSalary.topGrossSalary }, HR: { tupleSalary.hrName }");    

This has none of the disadvantages of out-parameters and the tuple type, but it’s rather verbose and it is meaningless overhead. Because, the properties inside a (class / struct) are not coming under one roof and not having any common base characteristics. For the purpose of creating temporary data structure C# has provided Tuples.

All three ways mentioned above has their own disadvantages, so they want to overcome these shortcomings by introducing a miracle.

Multiple return types in C# 7.0

Tuple return types:

You can specify multiple return types for a function, in much the same syntax as you do for specifying multiple input types. These are supposed to be called Tuple Types.

Public (double topGrossSalary,string hrName) GetMultipleValues(string name) {……….. }   

The syntax (double topGrossSalary,string hrName) indicates an anonymous struct type with public fields of the given names and types. Note that this is different from some notions of tuple, where the members are not given names but only positions (i.e Tuple<double, string>). This is a common complaint, though, essentially degrading the consumption scenario to that of System.Tuple above. For full usefulness, tuples members need to have names. This is also fully compatible with async.

public async Task<(double topGrossSalary, string hrName)> GetMultipleValues Async(string name) { ... }      var t = await GetMultipleValues (myValues);   Console.WriteLine($"Sum: {t.sum}, count: {t.count}");    

Tuple literals

Tuple values could be created as,

var t = new (int sum, int count) { sum = 0, count = 0 };   

Creating a tuple value of a known target type, should enable leaving out the member names.

public (int sum, int count) Tally(IEnumerable<int> values)    {   var s = 0; var c = 0;   foreach (var value in values) { s += value; c++; }   return (s, c); // target typed to (int sum, int count)   }   

In the above example we have created a method with return type of tuple type with known target type and constructed a tuple type inline using the previously declared individual variables (i.e. return(s, c)).

Using named arguments as a syntax analogy it may also be possible to give the names of the tuple fields directly in the literal:

public (int sum, int count) Tally(IEnumerable<int> values)    {   var res = (sum: 0, count: 0); // infer tuple type from names and values   foreach (var value in values) { res.sum += value; res.count++; }   return res;   }   

Tuple deconstruction

We don’t need to the tuple object as a whole because it doesn’t represent a particular entity or a thing, so the consumer of a tuple type doesn’t want to access the tuple itself, and instead he can access the internal values of the tuple.

Instead of accessing the tuple properties as in the example of Tuple Return Types, you can also de-structure the tuple immediately:

(var sal, var hrName) = GetMultipleValues("some address");   Console.WriteLine($"Salary: { sal }, Hr Name: {hrName}");   

Pattern Matching

Is Expression

The “is” operator can be used to test an expression against a pattern. As part of the pattern-matching feature repurposing the “is” operator to take a pattern on the right-hand-side.

relational_expression : relational_expression 'is' pattern;   

It is a compile-time error if the relational_expression to the left of the “is” token does not designate a value or does not have a type. Every identifier of the pattern introduces a new local variable that is definitely assigned after the “is” operator is true (i.e. definitely assigned when true).

Pattern

Patterns are used in the is operator and in a switch_statement to express the shape of data against which incoming data is to be compared.

There are many areas where we can use patterns in c#. You can do pattern matching on any data type, even your own, whereas if/else you always need primitives to match. Pattern matching can extract values from your expression.

For ex:I am having handful of types

class Person(string Name);      class Student(string Name, double Gpa) : Person(Name);      class Teacher(string Name, string Subject) : Person(Name);      //This sample uses the latest c# feature record type to create objects   

I wanted to perform some operations by type specific.

static string PrintedForm(Person p)   {   Student s;   Teacher t;   if ((s = p as Student) != null && s.Gpa > 3.5)   {   return $"Honor Student {s.Name} ({s.Gpa})";   }   else if (s != null)   {   return $"Student {s.Name} ({s.Gpa})";   }   else if ((t = p as Teacher) != null)   {   return $"Teacher {t.Name} of {t.Subject}";   }   else   {   return $"Person {p.Name}";   }   }   

Below is the client application which consumes the printed form function,

static void Main(string[] args)   {   Person[] oa = {   new Student("Einstein", 4.0),   new Student("Elvis", 3.0),   new Student("Poindexter", 3.2),   new Teacher("Feynmann", "Physics"),   new Person("Anders"),   };   foreach (var o in oa)   {   Console.WriteLine(PrintedForm(o));   }   Console.ReadKey();   }   

In the above sample for holding the objects, I need to create temporary variables s and t. So if I have n number of objects I need to create N temporary variables with distinct names unnecessarily, which make my code more verbose. And also the temporary variables are only need for a particular code block but it is having scope throughout the function. Note the need to declare variables s and t ahead of time even though it is used in one of the code blocks.

As part of the pattern-matching feature we are repurposing the “is” operator to take a pattern on the right-hand-side. And one kind of pattern is a variable declaration. That allows us to simplify the code like this,

static string PrintedForm(Person p)   {   if (p is Student s && s.Gpa > 3.5) //!   {   return $"Honor Student {s.Name} ({s.Gpa})";   }   else if (p is Student s)   {   return $"Student {s.Name} ({s.Gpa})";   }   else if (p is Teacher t)   {   return $"Teacher {t.Name} of {t.Subject}";   }   else   {   return $"Person {p.Name}";   }   }   

Now you can see that the temporary variables s and t are only declared and scoped just to the place they need to be. The switch statement is also repurposed like the case branches can also have patterns instead of just constants.

static string PrintedForm(Person p)   {   switch (p) //!   {   case Student s when s.Gpa > 3.5 :   return $"Honor Student {s.Name} ({s.Gpa})";   case Student s :   return $"Student {s.Name} ({s.Gpa})";   case Teacher t :   return $"Teacher {t.Name} of {t.Subject}";   default :   return $"Person {p.Name}";   }   }   

You can see in the above example that the case statements with patterns. Please note the new when key word in switch statement.

Record Types

Record Types is concept used for creating a type with only properties. By using that we can embed the constructor declaration with the class declaration.

For ex:

Class Student(string Name, int Age);   

This simple statement would automatically generate the following code inbuilt.

Class Student    {   string _name;   int _age;      public Person(string Name, int Age)   {   this.Name = Name;   this.Age = Age;   }   public string Name {get{ return this._name;}}   public int Age {get{ return this._age;}}   }    
  • Read-only properties, thus creating it as immutable type.
  • The class will automatically implement Equality implementations like (such as GetHashCode, Equals, operator ==, operator != and so forth).
  • A default implementation of ToString() method.

Non-Null able reference types:

Non- nullable reference option will let you create a reference type that is guaranteed not to be null. NullReference expections are too common in a project. Often we developers forgot to check a reference type for null before accessing the properties of it, thus paving way to problems.

Either we forget check for it making our code vulnerable to runtime exceptions or we will check for it which makes our code more verbose.

Instead of using the “?” for identifying the nullable value type we are going to use “!”.The currently proposed syntax is as follows:

int a; //non-nullable value type   int? b; //nullable value type   string! c; //non-nullable reference type   string d; //nullable reference type      MyClass a; // Nullable reference type   MyClass! b; // Non-nullable reference type      a = null; // OK, this is nullable   b = null; // Error, b is non-nullable   b = a; // Error, a might be null, b can't be null      WriteLine(b.ToString()); // OK, can't be null   WriteLine(a.ToString()); // Warning! Could be null!      if (a != null) { WriteLine(a.ToString); } // OK, you checked   WriteLine(a!.Length); // Ok, if you say so      It would be quite problematic using the same syntax for generic types and collections. For example   // The Dictionary is non-nullable but string, List and MyClass aren't   Dictionary<string, List<MyClass>>! myDict;       // Proper way to declare all types as non-nullable   Dictionary<string!, List<MyClass!>!>! myDict;   

For specifying all the arguments in a collection is non- nullable, there is a shortcut syntax has been proposed,

// Typing ! in front of the type arguments makes all types non-nullable   Dictionary!<string, List<MyClass>> myDict;   

Immutable Types

An immutable object is an object whose state cannot be changed after its creation, which means Immutable objects are objects which once loaded cannot be changed / modified by any way external or internal.

Immutable objects offer few benefits, 

  • Inherently thread-safe.
  • Easier to parallelize.
  • Makes it easier to use and reason about code.
  • Reference to immutable objects can be cached, as they won’t change.

Currently it is also possible to create immutable classes. Create a class with properties only with get and read-only and constant private variables.

Public class Point   {   public Point(int x, int y)   {   x = x;   Y = y;   }   public int X { get; }   public int Y { get; }   }   

Code in the above example is definitely an immutable class, but the intent of the class is not clearly stated as immutable. Because, in future any one can add setters to any of the properties thus making it as mutable.

The proposed syntax for creating an immutable class will force the developer to strictly adhere the rules hence will make the class an immutable. Below is the proposed syntax,

public immutable class Point   {   public Point(int x, int y)   {   x = x;   Y = y;   }      public int X { get; }   public int Y { get; }   }   

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » New features of C# 7.0

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址