Feature List in
C# 7.0
- Local functions – code
available currently in github
- Tuple Types and literals
- Record Types
- Pattern matching
- Non Nullable reference types
- Immutable types
- Local Functions
- Up to 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 Funtion c
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();
}
|
Evolution |
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.
-
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.
- Out parameters
- Tuple-Types
- 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-Nullable 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
|
Types of modelling in c# |
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; }
}