Pattern matching is a technique where you test an expression to determine if it has certain characteristics. 
C# pattern matching provides more concise syntax for testing expressions and taking action when an expression matches. 
The "is" supports pattern matching to test an expression and conditionally declare a new variable to the result of that expression. 
The "switch" enables you to perform actions based on the first matching pattern for an expression. 
These two expressions support a rich vocabulary of patterns.



1. Type Checking and Casting
     Use is to check an object's type and cast it safely in a single step.

//before
if (obj is string)
 {
     string s = (string)obj;
     Console.WriteLine($"length {s.Length}");
 }

//after
if (obj is string s)
{
    Console.WriteLine($"length {s.Length}");
}

2. Relational Patterns
    Use comparison operators (<, >, >=, <=) directly in pattern matching.

//before
if (age > 0 && age < 18)
{
    Console.WriteLine("Minor");
}


//after
if (age is > 0 and < 18)
{
    Console.WriteLine("Minor");
}

3. Logical Patterns
   Combine patterns using logical operators: and , or , not

//before
if (number != 0)
{
    Console.WriteLine("Non-zero");
}


//after
if (number is not 0)
{
    Console.WriteLine("Non-zero");
}

4. Positional Patterns
    Match values using tuple-like syntax or deconstruction of objects.

public class Point
{
    public int X { get; }
    public int Y { get; }
    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}


//before
if (point.X == 0 && point.Y == 0)
{
     Console.WriteLine("Origin");
}


//after
if (point is (0, 0))
{
    Console.WriteLine("Origin");
}


5. Property Pattern
     Match based on the values of specific properties in an object.

public class Person
{
    public int Age { get; set; }
    public string Name { get; set; }
    public Role Role { get; set; }
}

//before
if (person.Age > 18 && person.Name == "John")
{
    Console.WriteLine("Adult named John");
}


//after
if (person is { Age: > 18, Name: "John" })
{
    Console.WriteLine("Adult named John");
}
and 

 { Age: > 18, Name: "John" }

or 

 NOT supported within a single property pattern
 if (person is { Age: > 18 or < 0 }) ❌

not 

 { Age: not 0 }


(more example)

//before
if (obj is Person)
{
    Person p = (Person)obj;
    Console.Write($"{p.Name}, your role is {p.Role}.");
}


//after
if (obj is Person p)
{
    Console.Write($"{p.Name}, your role is {p.Role}.");
}

//before
if (person !=null && person.Age >= 18 && person.Role == Role.User)
{
    Console.WriteLine("Adult user detected.");
}



//after
if (person is { Age: >= 18, Role: Role.User })
{
    Console.WriteLine("Adult user detected.");
}

//before
if (person !=null && person.Role!= Role.Admin )
{
     Console.WriteLine("Access denied. Admins only.");
}



//after
if (person is not { Role: Role.Admin })
{
    Console.WriteLine("Access denied. Admins only.");
}

//before
if (person !=null && (person.Name== "Alice“ || person.age<18)
{
     Console.WriteLine("Access denied. Admins only.");
}



//after
if (person is { Name: "Alice" } or { Age: < 18 })
{
    Console.WriteLine("Special case or minor.");
}


6. Switch Expressions
    A concise way to match patterns and return values using switch

//before
string result;
if (obj is int i)
    result = $"It's an int: {i}";
else if (obj is string s)
    result = $"It's a string: {s}";
else if (obj == null)
    result = "It's null";
else
    result = "Unknown";


//after
string result = obj switch
{
    int i => $"It's an int: {i}",
    string s => $"It's a string: {s}",
    null => "It's null",
    _ => "Unknown"
};


//Example 2
string result = person switch
{
    { Role: Role.Admin } => "Welcome, Admin!",
    { Age: < 18 } or { Age:<4} => “Just older than 18 or younger than 4",
    { Name: "Alice" } => "Hi Alice!",
    { Name: var name } when name.StartsWith("A")=>"Name starts with A."
};


7. List Pattern Matching 
    Match elements in arrays or lists by position, length, and values.

int[] numbers = { 1, 2, 3 };

//before
if (numbers.Length == 3 && numbers[0] == 1 && 
                           numbers[1] == 2 && 
                           numbers[2] == 3)
{
    Console.WriteLine("Exact match!");
}


//after
if (numbers is [1, 2, 3])
{
    Console.WriteLine("Exact match!");
}



//Example 2
int[] scores = { 100, 90, 80 };

if (scores is [100, ..])
{
    Console.WriteLine("Starts with a perfect score!");
}


//Example 3 (more complex)
List<Person> people = GetPeople(); 
if (people is [
                { Age: > 20 },           //First item
                _, _, _, { Age: >= 30 }, //5th item must be Age >= 30
                ..,                      //Skip to the rest
                { Name: “Vahid”}         //Last item
              ] and 
              { Count: > 20 })  //List must have more than 20 elements
{
    Console.WriteLine("Matched pattern!");
}