📜 ⬆️ ⬇️

Functional F #, which slowly appears in C #


For some reason, we often do not use this functionality. Maybe they didn’t have time to get used to it. And sometimes we use, without having any idea that this is a functional from F #.

Before proceeding to its consideration, let's quickly run over the jack with a small retrospective of the most interesting features that appeared in different years in different versions of the language. Please note that each time a new version of the language comes out with a new version of Visual Studio. For some, this may be obvious, but even for developers who have spent several years working with C # this may turn out to be news (not everyone pays attention to this).

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 the indication of the name of the property. 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)); } 

In the same way, you can organize logging. Get the name of the method from which the call comes all the same attribute CallerMemberName. In the same way, you can get the file name and line number using [CallerFilePath] and [CallerLineNumber]. By the way, these attributes can be used in F #, but it's not about them.

Speaking of new features that have appeared in C #, lately it’s impossible not to mention the “intervention” of F #, which started from the 6th version of the language. It all started with the beloved LINQ. Here is a list of only some of the features that appeared in C #: LINQ, Immunity, Exception filters, Auto-property initializers, Expression-bodied functions, Pattern matching, Tuples
Apparently, even if you do not start learning F # in the near future, you will soon become more familiar with functional programming. 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:

  public string Name { get;} 

In this way, it becomes possible to create your immorable objects in a comfortable way.

Exception filters


And this is an opportunity when “catching” errors to specify the parameter at which the capture will work. For example:

  try { SomeMethod(param); } catch (Exception e) when (param == null) { } catch (Exception e) { } 

In the first block, the error will be caught only if param == null. The second will get errors that occurred when the param values ​​are non-null.

Auto-property initializers


This is an opportunity to initialize the property immediately after access methods (accessors). For example:

  public string AppUrl { get; set; } = "http://lalala.com"; 

Or you can even take out the 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


Convenient ability to reduce code using 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"; } } 

Now you can write much shorter:

  public int Sum(int x, int y) => x+y; public string ServerIP => "65.23.135.201"; 

All these features just appeared in version 6 of C #.
Now let's analyze a little more detail what came from F # in the 7th version of the language.

Tuples or Tuples


This functionality, in the opinion of many, can be compared with LINQ in terms of importance and usability.
What is he giving 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 transfer several parameters to the method, but only one thing will be returned. I repeat that the typical solution for this need so far has been the creation of an instance of the class. Now we can create a tuple.
A typical example of a tuple:

  var unnamed = (35, "What is your age?"); 

As you can see, this is nothing but a variable that contains two values ​​in parentheses. In this case, the tuple is called unnamed and values ​​can be addressed by the name Item with a number. For example, unnamed.Item1 contains 35, and unnamed.Item2 contains the string “What is your age?”

If someone notices that the tuples are similar to anonymous types, it will be right. But there is a nuance that you should not forget. 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 contains 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 whose first value is an integer of type int, and the second is a string.
We get the value in a variable like this:

  var xy = GetXY(); 

Now we can refer to the elements of the tuple by xy.Item1 and xy.Item2

Tuples have such an interesting feature as deconstruction / deconstruction. This is getting the usual variable values ​​from the tuple. Well, or you can tell the decomposition of the tuple into separate variables. For example, there is such a method that returns a list and some kind of ID that belongs to this list:

  (int, List<string>) GetListWithId() { int x = 1; List<string> y = new List<string>(); return (x, y); } 

In order to get the values ​​immediately in the form of variables, and not in the form of a tuple, you can use one of the following two methods:

  (int x, List<string> y) = GetListWithId(); var (x, y) = GetXY(); 

In C # version 7.1, a feature called infer tuple names appeared. It is possible to translate into Russian approximately as: assumed names of tuples. This means that it will be possible to refer to an unnamed element of a tuple not only by Item1, Item2, etc., but also by name, which is formed based on the name of the variable that participated in the creation of the tuple. Example:

  var tuple = (xa, y); 

Elements of this tuple can be accessed by the names tuple.a and tuple.y

What is interesting is that this 7.1 change is breaking back compatibility. That is back incompatible. But since the gap between the output of C # 7 and C # 7.1 is small, they decided to enter it. The essence of what. 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 a code in a real project is extremely small.
Example. Suppose you have this code:

  int x=1; Action y = () => SomeMethod(); var tuple = (a: x, y); tuple.y(); 

Notice the last line, which obviously calls a method called SomeMethod (and it does call this method, but only starting from C # 7.1)
So here. In C # 7.0, not SomeMethod would be called, but an extension method named y. Suppose 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 are using a project with a version of the .NET Framework lower than 4.7, then you can use tuples only by installing the NuGet System.ValueTuple package. However, Visual Studio 2017 should prompt you for this if you start using tuple syntax. Maybe that's why tuples are not used so often yet. 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 contains surprisingly fairly simple functionality.
Let's look at the sample 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 one cannot say that there is something wrong in it. However, now it is possible to reduce it a bit:

  if (someObject is Customer c) c.Balance = c.Balance + 1000; 

It turns out that if someObject belongs to the class Customer, then it is converted to a variable with the type Customer. And you can immediately start working with this variable.
You can add that pattern matching is not only syntactic sugar, but also the opportunity for developers who have dealt with similar constructs 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; } 

In addition, you can make clarifications using conditions with

  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 proposed innovations in C # 8 there is a functional that appeared not so long ago in Java. Namely: default interface methods. In particular, using this functionality, it will be possible to change the legacy code. For example, Java developers themselves managed using this functionality to improve the collections API and add support for lambda expressions. Maybe C # developers also want to change something in C # itself using this functionality?

Despite the similarity that has appeared, the interfaces are still quite different from abstract classes. Multiple inheritance in C # was abandoned for a long time due to the multitude of difficulties encountered. 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, the class must specify the name of the method with the specification of the interface name. Tracking only one method is easier (in particular, using an IDE).

The C # language is used in quite a large number of different types of projects. Despite the fact that it does not occupy the first line in popularity, it is unique in the breadth of the types of projects. Web development, desktop, cross-platform, mobile, games ... Including some features of functional programming or other languages ​​developing in parallel, C # is becoming more versatile. Moving to C # from another language is getting easier. However, it is also easier for experienced C # developers to understand the syntax of other languages.

Source: https://habr.com/ru/post/339592/


All Articles