Written by 12:28 ASP .NET CORE, Basics of C#, Languages & Coding • One Comment

Functional F# that slowly appears in C#

For some reason, we often do not use this functionality. Maybe we haven’t got used to it yet. And sometimes we use it, having no idea that this is the functionality from F#.

Before reviewing it, let’s quickly run through the most interesting features that appeared in different versions of the language. Note that each time a new version of the language comes out with a new version of Visual Studio. For someone, this may be obvious, but even for developers who have worked with C# for several years, this may turn out to be a piece of news (not everyone takes notice of it).

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 some are used constantly. For example, even now you can quite often use OnPropertyChanged with the indication of the property name, that is, something like OnPropertyChanged(“Price”). Although starting from the 5th version of the language, it has become 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));
   }

Logging can be done in the same way. The CallerMemberName attribute will help to get the name of the method from which the call is made. 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 that is not the point.

Speaking about the new features that appeared in C#, I cannot but mention the “intervention” of F#, which started from the 6th version of the language. It all started with everyone’s favorite LINQ. Here is the list of some features that appeared in C#: LINQ, immutability, Exception filters, Auto-property initializers, Expression-bodied function members, Pattern matching, and Tuples.

Apparently, even if you do not start to study F# in the near future, soon functional programming will become a bit familiar to you. Let’s look at some of the “functional” features of C#.

Immutability

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 unchangeable by default. How can this be implemented in C#? Starting with version 6, we can create read-only properties without specifying the set. For example, this:

public string Name { get;}

Thus, it becomes possible to easily create your own immutable objects.

Exception filters

This is an opportunity to specify a parameter with which the capture will work when “catching” errors. For example, like this:

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 one will deal with errors that occur when the values of param are different from null.

Auto-property initializers

This is the ability to initialize a property right after access methods. For example, like this:

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

Alternatively, you can even bring the initialization to 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 the code with lambda expressions. Methods that used to be written like this:

public int Sum(int x, int y)
   {
     return x+y;
   }

public string ServerIP { get { return "65.23.135.201";  } }

can be written much shorter  now:

 public int Sum(int x, int y) => x+y;

 public string ServerIP => "65.23.135.201";

All these listed features appeared in the 6th version of C#.

Now, let’s review in details what came from the 7th version of F#.

Tuples

This functionality is considered to be comparable with LINQ in terms of importance and ease of use.

What does it give us? First of all, this is the ability to return several values from the method, without creating a copy of any class. While several parameters can be passed to the method, only one will be returned. A typical solution for this requirement has been the creation of a class instance so far. Now, we can create a tuple.

A typical example of a tuple is:

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

As you can see, this is nothing more than a variable containing two values in parentheses. In this case, the tuple is called unnamed and the values can be accessed by the name of 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 tuples are similar to anonymous types, then they will be right. However, there is a nuance, we should not forget about. Anonymous types can only be used in the scope of a method.

There can be named tuples.

var named = (Answer: 35, Question: "What’s your age again?");

Variables from a named tuple can be addressed by names. In this case, named.Answer stores 35, and named.Question stores the string with the text of the question.

The simplest example is as follows. A method that returns a value as 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, while the second one is a string.

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.

There is such an interesting feature as deconstruction in tuples.

This is when regular variables receive values from a tuple. In other words, this is the decomposition of the tuple into separate variables. For example, there is such a method that returns a list and some ID belonging 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 as variables, rather than as a tuple, we can use one of the following two methods:

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

In C# 7.1, there is an option called infer tuple names. This means that you can refer to an unnamed element of a tuple not only by Item1, Item2, etc., but by the name, which is derived from the variable name that participated in the creation of the tuple. Example:

var tuple = (x.a, y);

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

What’s interesting is that this change in 7.1 is a breaking back compatibility, i.e. it is back incompatible. However, since the gap between the release of C# 7 and C# 7.1 is small, they decided to introduce this. What is the essence? Some code that worked in a certain way in C# 7 will work differently in C# 7.1.

In this case, the possibility of finding such a code in a real project is extremely small.

For example, let’s say you have this code:

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

Pay attention to the last line, which obviously calls the SomeMethod method (and it does call this method, but only starting from C# 7.1)

In C#, an extension method named y would be called instead of SomeMethod. 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 a rarity.

If you are using a project with .NET Framework lower than 4.7, then you can use the tuples only by installing the NuGet System.ValueTuple package. However, Visual Studio 2017 should suggest you this if you start using the tuple syntax. Maybe that’s why tuples are still not used so often. 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

This rather ambiguous name contains a surprisingly simple functionality.

Let’s look at an example 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 perfectly typical, common and one cannot say that there is something wrong with it. However, now, there is a possibility to slightly reduce the code size:

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

It turns out that if someObject belongs to the Customer class, it is converted to a variable of the Customer type. You can start working with this variable right away.

Pattern matching is not only syntax sugar but also the ability for developers who deal with similar constructs in other languages to use them in C#.

Pattern matching can be used not only with if but also with Switсh constructs. Example:

switch (userRole)
    {
        case Manager m:
            return m.Salary;
        case Partner p:
            return p.Income;
    }

In addition, you can 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;
    }

The first case will be executed only if userRole is the manager and the value of its Salary is less than 1500. That is, m.Salary <1500.

In the list of proposed innovations in C# 8, there is a functionality that has recently appeared in Java. Namely: default interface methods. In particular, using this functionality, you can change the legacy code. For example, Java developers managed to improve the API collections using this functionality and add support for lambda expressions. Maybe the developers of C# also want to change something in C# with the help of this functionality.

Despite the similarity that appeared, the interfaces are still quite different from the abstract classes. They abandoned multiple inheritance in C# long ago because of the multitude of emerging difficulties. In this case, using only one method, the complexity should be less. In case the 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. It is easier to track only one method (in particular with the help of IDE).

The C# language is used in quite a large number of different types of projects. Despite the fact that it is not that popular, it is unique in the coverage of the project types. Web development, desktop, cross-platform, mobile, games… Due to certain features of functional programming or other developing languages, C # becomes universal. It’s easier to switch to C# from another language. In turn, it is easier for the experienced C# developers to understand the syntax of other languages.

Tags: , , , Last modified: April 13, 2022
Close