Intro To Pattern Matching – Covers C# 9

栏目: IT技术 · 发布时间: 4年前

内容简介:Pattern matching is an essential and powerful building block to many functional programming languages like Haskell or Scala. Pattern matching allows the developer to match a value (or an object) against some patterns to select a branch/block of the code.I

What is Pattern Matching?

Pattern matching is an essential and powerful building block to many functional programming languages like Haskell or Scala. Pattern matching allows the developer to match a value (or an object) against some patterns to select a branch/block of the code.

I have used the Type Pattern to demonstrate the concept of the pattern matching,

inputData:  
1-  Does inputData match with a string?  
2-  Does inputData match with an int?  
3-  Or it is something else?    switch(inputData)  
switch(inputData)  
{  
  case string strBinded: {Execute the code}; 
  case int intBinded :{Execute the code};  
  case _ :  {Execute else code}  
}  

As you can see above, the type pattern can be used as a replacement for type check and type cast.

You can consider pattern matching as a replacement for if-else and the classic switch cases. But in the end, it is still another type of if-else.

Pattern Matching Core Concept

Intro To Pattern Matching – Covers C# 9

Figure -1- OOP example

Consider the following example,

public class Car  
{  
  private int fuel;  
  private int speed;  
   
  public Car(int fuel, int speed)  
  {  
    this.fuel = fuel;  
    this.speed = speed;  
  }  
   
  public void Deconstruct(out int fuel, out int speed)  
  {  
    fuel = this.fuel;  
    speed = this.speed;  
  }  
   
  public void SetFuel(int value)  
  {  
    this.fuel = value;  
  }  
   
  public int GetFuel()  
  {  
    return this.fuel;  
  }  
   
  public void SetSpeed(int value)  
  {  
    this.speed = value;  
  }  
   
  public int GetSpeed()  
  {  
    return this.speed;  
  }  
   
  public void Drive()  
  {  
    // Bohn Bohn brunn brunn  
    this.speed = this.speed + 10;  
    this.fuel = this.fuel - 5;  
  }  
}   
The object in the Object-Oriented Programming is encapsulated in one unit (the state and the behavior), the data structures, and the methods which work on those data structures. If you need new functionality, then we have to add a new method to the object definition. For example, you need to stop the car, then you have to add the Stop() {} to the class Car . If you want to add a few methods, then this approach is working fine. The problem comes when you want to add continually new methods all the time. You might realize that you have already written dozens of methods, and you have to add more methods like EngineOn(){}, Break(){}, and TurnLightOn(){} … etc. That can be quite a challenge to keep the code still maintainable. Pattern matching provides an alternative way to handle those problems.

The idea is simple.

Constructor

In the constructor, you pass the data structure (parameters) to collect and create the object.

public Car(int fuel, int speed)   

Deconstruct

Destructs an object to a tuple. It takes the data structures out of the object.

public void Deconstruct(out int fuel, out int speed)  
Intro To Pattern Matching – Covers C# 9

Figure -2- OOP vs. FP

As shown, in the image above, it becomes easy to perform the Car operations on the deconstructed data structures, and you can add the new operations(methods) easily. The core concept is, when you have a stable and a known data structure, it’s often very interesting to apply the pattern matching approach because you can quickly expand the operations. However, if your operations are stable, but the data changes, then the OOP approach seems more adequate.

Why is Pattern Matching so important in C#?

The first reason is that functional programming can be complementary to the OOP paradigm, and in recent history, we have seen many successful technologies based on the combination between OOP and FP like LINQ. The second reason is more political. C# is one of the top 10 languages. The world is changing very fast, and therefore, functional programming over time becomes more important than any other approach. The .Net development team could not tolerate the idea of losing their success; for this reason, they have decided to work aggressively in the innovation world. They are trying to expand everywhere, adding more new features and programming paradigms, and they are trying to make C# more performant and simplifying the syntax. I was not surprised when I saw the C# development team brings pattern matching and records to the center of the C# programming style. I suppose, with C# 10, we can do everything in a functional way. This aggressive development strategy is good and evil. One thing I have noticed in the last few years is that many C# developers are angry about that. Furthermore, the F# developers are very disappointed about including FP in C# and not empowering F#.

At this moment, F# evangelism and F# prophets became hopeless, and they are trying to compare the language syntaxes, and they are assuming that F# concepts deserve better.

Recap

C# 7.x

  • Constant patterns
  • Type patterns
  • Var patterns
  • Destruction
  • Pattern matching on generic type parameters

C# 8

  • Pattern matching enhancements:
  • Switch expressions
  • Property patterns
  • Tuple patterns
  • Positional patterns

C# 9 probably

  • Type patterns
  • Parenthesized patterns
  • Relational patterns
  • Combinator Patterns
    • Conjunctive and patterns that require both of two different patterns to match.
    • Disjunctive or patterns that require either of two different patterns to match.
    • Negated not patterns that require a given pattern not to match.

I recommend you to read my old article about Pattern Matching,

https://www.infoq.com/articles/cs8-ranges-and-recursive-patterns/

!!IMPORTANT!!

The plan and the C# 9 proposals, still in the draft stage, have not yet been put into the final form for a decision by the .Net development team. The syntax might be changed, or the below proposal might be removed from the C# 9 milestone. 

C# 9 Pattern Matching

The .NET development team is considering a small handful of enhancements to pattern matching for C# 9.0 that have a natural synergy and work well to address several common programming problems,

  • Type patterns  is used to match the input against a type. If the input type is a match to the type specified in the pattern, the match succeeds.
  • Parenthesized patterns  permit the programmer to put parentheses around any pattern.
  • Relational patterns  permits the programmer to express that an input value must satisfy a relational constraint when compared to a constant value.
  • Combinator Patterns  permits the programmer to combine multiple patterns on one line, with AND/OR operators, or to negate a pattern by using the NOT operator,
    • Conjunctive and patterns that require both of two different patterns to match.
    • Disjunctive or patterns that require either of two different patterns to match.
    • Negated not patterns that require a given pattern not to match. 

Type Patterns

In C# 7, we have seen that the is-type operator extends to be an is expression. C# 9 introduces a new type pattern, which can also be used to match the input against a type. This pattern is nothing more than a minor syntactic convenience.

Example

nt age = 42;  
string name= "Bassam";  
var userTuple = (age, name);  
  
// Current version  
// test if age is an int and name is a string  
if (userTuple is (int _, string _) )   
{  
  Console.WriteLine(userTuple.name);  
}  
  
// C# 9  
// test if age is an int and name is a string  
if (userTuple is (int, string) )   
{  
  Console.WriteLine(userTuple.name);  
}  

Compare the C# Syntax trees,

Current C# version,

Intro To Pattern Matching – Covers C# 9

Figure -3- Syntax tree for: if (userTuple is (int _, string _) )

C# 9

Intro To Pattern Matching – Covers C# 9

Figure -4- Syntax tree for: if (userTuple is (int, string) )

Comparing the IL Code for Pattern Matching

Produced IL Code

// int age = 42;  
IL_0000: ldc.i4.s 42  
IL_0002: stloc.0  
  
// string name= "Bassam";  
IL_0003: ldstr "Bassam"  
IL_0008: stloc.1  
  
// var userTuple = (age, name);  
IL_0009: ldloca.s 2  
IL_000b: ldloc.0  
IL_000c: ldloc.1  
IL_000d: call instance void valuetype [System.Private.CoreLib]System.ValueTuple`2<int32, string>::.ctor(!0, !1)  
  
// if (userTuple is (int _, string _) ) // Current Version  
IL_0012: ldloc.2  
IL_0013: ldfld !1 valuetype [System.Private.CoreLib]System.ValueTuple`2<int32, string>::Item2  
IL_0018: brfalse.s IL_0025  
  
// System.Console.WriteLine(userTuple.name);  
IL_001a: ldloc.2  
IL_001b: ldfld !1 valuetype [System.Private.CoreLib]System.ValueTuple`2<int32, string>::Item2  
IL_0020: call void [System.Console]System.Console::WriteLine(string)   
  
// if (userTuple is (int, string) ) // C# 9  
IL_0025: ldloc.2  
IL_0026: ldfld !1 valuetype [System.Private.CoreLib]System.ValueTuple`2<int32, string>::Item2  
IL_002b: brfalse.s IL_0038  
  
// System.Console.WriteLine(userTuple.name);  
IL_002d: ldloc.2  
IL_002e: ldfld !1 valuetype [System.Private.CoreLib]System.ValueTuple`2<int32, string>::Item2  
IL_0033: call void [System.Console]System.Console::WriteLine(string)

Pattern Combinators

Permits matching both of two different patterns using and/or operators or the negation of a pattern by using not operator.

Pattern Combinators consist of the following sub-patterns,

  • Conjunctive and patterns that require both of two different patterns to match.
  • Disjunctive or patterns that require either of two different patterns to match.
  • Negated not patterns that require a given pattern not to match. 

AND “Conjunctive” Pattern

Permits the AND Logics on two different patterns.

Example

if (o is int and 1) { } // The code block will be executed if the o is of type int and its value is 1  

OR “Disjunctive” Pattern

Permits the OR Logics on two different patterns.

Example

if (o is (1 or 2) and int x4) { } // The code block will executed if the value of o is 1 or 2   

Negated Pattern

Permits the NOT Logics on a pattern.

Example

if (e is not null) { }  
The code will be executed when e is a reference type and not null. I love It! And I hope the keyword not will not replace with the ugly operator ~.

Neal Gafter example:

The and or combinators will be useful for testing ranges of values. 

bool IsLetter(char c) => c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';  

Examples

if (o is int x1 and 1) { }  
if (o is int x2 and (1 or 2)) { }   

The code block will be executed if the o is of type int, then (copy o to x2), and x2 value must be 1 or 2. As we can see above, we have also used Parenthesized patterns. Unfortunately, the Range Pattern is declined; in other words, you cannot do in C# 9:     The alternative for that is Relational Patterns, which is described in the

section below. 

if (o is 1 and int x3) { }  
if (o is not (1 or 2) and int x5) { }  

Parenthesized patterns

According to the documents, the Parenthesized patterns can be grouped around patterns to achieve the desired associativity. In the following example, parentheses are used to control associativity between AND pattern and Or pattern.

Back to the Neal Gafter example:

The and and or combinators will be useful for testing ranges of values.

bool IsLetter(char c) => c is >= 'a' and <= 'z' or >= 'A' and <= 'Z'; // and & or combinators  

The below example illustrates that the and-operator will have a higher parsing priority (i.e. will bind more closely) than the or-opreator . The programmers can use the parenthesized pattern to make the precedence explicit:

bool IsLetter(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');  

Relational patterns

Allows the case-labels in switch-statement to support the comparison operators, much like the Select Case statement in Visual Basic.

Supported operators and types

==, !=, <, <=, >, and >= on all of the built-in types that support such binary relational operators with two operands of the same type in an expression. Specifically, it supports all of these relational patterns for sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, and decimal. Additionally, it supports == and != for string and bool.

Back to Neal Gafter Examples: 

bool IsHexLetter(char c) => c is in 'A' to 'Z' or in 'a' to 'z';  
The 'to' keyword would be used to specify a range. In this case [A..Z] or [a..z].

Switch-label

So, you could write code as in this example:

int iq = DoIqTest();  
   
switch (iq)  
{  
    case <= 69:  
        ProcedureExtremelyLow();  
        break;  
    case 70 to 79:  
        BorderlineProcedure();  
        break;  
    case 80 to 89:  
        LowAverageProcedure();  
        break;  
    case 90 to 99:  
    case 101 to 109:  
        AverageProcedure();  
        break;  
    case 100:  
        ExactlyMedianProcedure();  
        break;  
    case 110 to 119:  
        HighAverageProcedure();  
        break;  
    case 120 to 129:  
         SuperiorProcedure();  
         break;  
    case >= 130:  
        VerySuperiorProcedure();  
        break;  
}  
The to keyword would be used to specify a range. In the statement switch (value), case x to y: would be equivalent to the boolean expression value >= x && value <= y .

Summary

The term pattern matching is a central feature of functional programming languages. This approach can be used to solve many programming problems efficiently. C# 9 and 10 will support the most F# Pattern Matching concepts.


以上所述就是小编给大家介绍的《Intro To Pattern Matching – Covers C# 9》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

CASIO fx-5800P编程计算器公路与铁路施工测量程序

CASIO fx-5800P编程计算器公路与铁路施工测量程序

2011-8 / 40.00元

《CASIO fx-5800P 编程计算器公路与铁路施工测量程序(第2版)》内容简介:第2版是一本全新的图书。书中的QH2-7T与QH2-8T程序都具有三维中边桩坐标正、反算,路基超高及边桩设计高程计算,边坡坡口与坡脚计算,桥墩桩基坐标计算,隧道超欠挖计算等功能。QH2-7T为交点法程序,QH2-8T为线元法程序,两个程序均使用数据库子程序输入平竖曲线的全部设计数据。测试程序各项功能所用的案例均取......一起来看看 《CASIO fx-5800P编程计算器公路与铁路施工测量程序》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码