
Functional F #, which is slowly appearing in C #

For some reason, we often do not use this functionality. Maybe they haven’t gotten used to it yet. And sometimes we use it, without having an idea that this is a functional from F #.
Before proceeding to its consideration, let's quickly go over the
Retrospective
C # 1.0 Visual Studio 2002
C # 1.1 Visual Studio 2003 - #line, pragma, xml doc comments
C # 2.0 Visual Studio 2005 -Generics , Anonymous methods, iterators / yield, static classes
C # 3.0 Visual Studio 2008 - LINQ , Lambda Expressions, Implicit typing, Extension methods
C # 4.0 Visual Studio 2010 - dynamic, Optional parameters and named arguments
C # 5.0 Visual Studio 2012 - async / await , Caller Information, some breaking changes
C # 6.0 Visual Studio 2015 - Null-conditional operators, String Interpolation
C # 7.0 Visual Studio 2017 - Tuples , Pattern matching, Local functions
Some functionality is rarely used, but something is used constantly. Say, even now, quite often you can still find the use of OnPropertyChanged with a property name. That is, something like OnPropertyChanged ("Price"); Although already from the 5th version of the language it became possible to get the name of the called object using CallerMemberName.
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
You can organize logging in the same way. The same CallerMemberName attribute will help to get the name of the method from which the call occurs. In the same way, you can get the file name and line number using [CallerFilePath] and [CallerLineNumber]. By the way, these attributes can also be used in F #, but we are not talking about them.
Speaking about the new features that appeared in C #, one cannot but mention the “intervention” F #, which started from the 6th version of the language. It all started with everyone beloved LINQ. Here is a listing of just some of the features that have appeared in C #: LINQ, immunity, Exception filters, Auto-property initializers, Expression-bodied function members, Pattern matching, Tuples
Apparently, even if you do not begin to study F # in the near future, soon functional programming will become a little familiar to you. Let's look at some of the “functional” features of C #.
Immunity
This is nothing but the immutability of objects. That is, the value of the object cannot be changed after creation. In F #, all variables are immutable by default. How can this be implemented in C #? Starting with version 6, you can create read-only properties without specifying set. For example, like this:
public string Name { get;}
Thus, it becomes possible to comfortably create your immutable objects.
Exception filters
And this is an opportunity to "catch" errors to specify the parameter at which the capture will work. For example, like this:
try
{
SomeMethod(param);
}
catch (Exception e) when (param == null)
{
}
catch (Exception e)
{
}
In the first block, an error will be caught only if param == null. The second will get errors that occurred with param values other than null.
Auto-property initializers
This is the ability to initialize a property immediately after access methods (accessors). For example, like this:
public string AppUrl { get; set; } = "http://lalala.com";
Or you can even put initialization in a separate method that returns a value.
public string AppUrl { get; set; } = InitializeProperty();
public static string InitializeProperty()
{
return "http://lalala.com ";
}
Expression-bodied function members
A convenient way to shorten code with lambda expressions. Methods that were previously written like this:
public int Sum(int x, int y)
{
return x+y;
}
public string ServerIP { get { return "65.23.135.201"; } }
can now be written much shorter:
public int Sum(int x, int y) => x+y;
public string ServerIP => "65.23.135.201";
All of these features just listed appeared in the 6th version of C #.
Now let's take a closer look at what came from F # in the 7th version of the language.
Tuples or Tuples
This functionality, according to many, can be compared with LINQ in terms of importance and usability.
That in itself he gives us. First of all, it is the ability to return several values from a method without creating an instance of any class. Because we can pass several parameters to the method, but only one can be returned. Once again, the typical solution for this need so far has been to instantiate the class. Now we can create a tuple.
A typical tuple example:
var unnamed = (35, "What is your age?");
As you can see, this is nothing more than a variable that contains two values in brackets. In this case, the tuple is called unnamed and the values can be accessed by the name Item with a number. For example, unnamed.Item1 contains 35, and unnamed.Item2 contains a line with the text “What is your age?”
If someone notices that the tuples are similar to anonymous types, then he will be right. But there is a nuance that should not be forgotten. Anonymous types can only be used in the scope of a method.
There are named tuples.
var named = (Answer: 35, Question: "What’s your age again?");
Variables from a named tuple can be accessed by name. In this case, named.Answer stores 35, and named.Question stores a string with the text of the question.
The simplest example. A method that returns a value in the form of a tuple:
(int, string) GetXY()
{
int x = 1;
string y = "One";
return (x, y);
}
The return value of the method is a tuple, the first value of which is an integer of type int, and the second is a string.
We get the value in the variable like this:
var xy = GetXY();
Now we can access the elements of the tuple by xy.Item1 and xy.Item2 Tuples
have such an interesting feature as deconstruction / deconstruction. This is getting regular variables from a tuple. Well, or we can say the decomposition of the tuple into separate variables. For example, there is such a method that returns a list and some ID-shnik related to this list:
(int, List) GetListWithId()
{
int x = 1;
List y = new List();
return (x, y);
}
In order to get the values immediately in the form of variables, and not as a tuple, you can use one of the following two methods:
(int x, List y) = GetListWithId();
var (x, y) = GetXY();
C # version 7.1 introduced an option called infer tuple names. You can translate into Russian approximately, like: the alleged names of tuples. This means that it will be possible to refer to an unnamed element of the tuple not only by Item1, Item2, etc., but also by name, which is formed based on the name of the variable that took part in the creation of the tuple. Example:
var tuple = (x.a, y);
Elements of this tuple can be accessed by the names tuple.a and tuple.y
What is interesting is that this change 7.1 is breaking back compatibility. That is backwards incompatible. But since the gap between the release of C # 7 and C # 7.1 is small, we decided to introduce it. The point is. Some code that worked in a certain way in C # 7 will work differently in C # 7.1.
In this case, the opportunity to meet such code in a real project is extremely small.
Example. Let's say you have a code like this:
int x=1;
Action y = () => SomeMethod();
var tuple = (a: x, y);
tuple.y();
Pay attention to the last line, which obviously calls a method called SomeMethod (and it does call this method, but only starting with C # 7.1)
So. In C # 7.0, not SomeMethod would be called, but an extension method called y. Let's say this:
public static class ExtClass
{
public static void y(this (int, Action) z)
{
// some code
}
}
Agree that extension methods for tuples are rare.
If you use a project with a version of the .NET Framework lower than 4.7, then you can only use tuples by installing the NuGet package System.ValueTuple. However, Visual Studio 2017 should tell you this yourself if you start using the tuple syntax. Maybe that’s why tuples are still not so commonly used. It is necessary not only to work in the latest version of Visual Studio, but also on one of the latest versions of the framework (or install the NuGet package).
Pattern matching
Under this rather ambiguous name, a surprisingly simple functionality is contained.
Let's look at an example of the code that was used earlier and, of course, can be used now:
if (someObject is Customer)
{
var c = (Customer)someObject;
c.Balance = c.Balance + 1000;
}
The code is completely normal, typical, often used and it cannot be said that something is wrong with it. However, now there is an opportunity to reduce it a little:
if (someObject is Customer c) c.Balance = c.Balance + 1000;
It turns out that if someObject belongs to the Customer class, then it is converted to a variable of type Customer. And with this variable you can immediately start working.
We can add that pattern matching is not only syntactic sugar, but also the ability for developers to deal with similar designs in other languages to use them in C #.
Pattern matching can be used not only with if, but also with Swith constructs. Example:
switch (userRole)
{
case Manager m:
return m.Salary;
case Partner p:
return p.Income;
}
You can also make refinements using the with conditions
switch (userRole)
{
case Manager m with Salary<1500:
return m.Salary*1.2;
case Manager m:
return m.Salary;
case Partner p:
return p.Income;
}
In this case, the first case will be executed only if userRole is a manager and its Salary value is less than 1500. That is, m.Salary <1500
In the list of supposed innovations in C # 8 there is a functionality that recently appeared in Java. Namely: default interface methods. In particular, with the help of this functionality it will be possible to change the legacy code. For example, the Java developers themselves were able to use this functionality to improve the collection APIs and add support for lambda expressions. Maybe C # developers also want to change something in C # itself using this functionality?
Despite the similarities that appeared, the interfaces are still quite different from the abstract classes. Multiple inheritance has been abandoned in C # for a long time due to the many difficulties that arise. But in this case, using only one method, the difficulties should be less. If a class inherits from two or more interfaces and there is a method with the same name in several interfaces, then the class must specify the name of the method with the name of the interface. Tracking only one method is easier (in particular, using the IDE).
The C # language is used in a fairly large number of different types of projects. Despite the fact that it does not occupy the first line in popularity, it is unique in its breadth of types of projects. Web development, desktop, cross-platform, mobile, games ... Including some features of functional programming or other C # languages developing in parallel, it becomes more universal. Switching to C # from another language is becoming easier. However, experienced C # developers, in turn, find it easier to understand the syntax of other languages.