Friday, June 26, 2026

C# Examples of switch statements and switch expressions

Instead of using multiple if conditions, switch statement can be used to make code more readable. 
Some Facts and features of switch case:
  • The switch statement does not return any value.
  • The switch keyword is followed by an expression inside parentheses.
  • The expression is evaluated for its value or type etc. with case label.
  • Inside switch block, there are case statements.
  • Each case is followed by a label. 
    • The label can be constant or pattern.
    • The label can be either a literal value e.g. 1, 4, 7.8, true, false, tuple values etc. 
    • Or, it can be a data type e.g. int, float, double etc.
    • Or, it can be data type followed by a variable for that type.
    • Or, it can be data type followed by a variable of that type and constraint on the variable using when clause.
  • Each case must end with a jump statement (break, return, throw, etc).
  • The break statement is not mandatory in switch statement. Instead, you can use return or throw statement also.
  • The default keyword is used for default case. It represents the case not found in other cases. It can be placed any order. It should not necessarily be the last case.
  • The default case is optional.
  • You can also pass delegate variable in switch expression.
In brief, a switch statement executes one matching case block. Cases can use constants or patterns. Each case must end with a jump statement (break, return, throw, etc.) to prevent fall-through. default is optional and may appear anywhere.

List of Examples
  • Example. Case exact matching constant (constant pattern match)
  • Example. Case of inexact match with relational operator(>,<,>=,<=) (constant pattern match)
  • Example. Case of matching with not operator(constant pattern match)
  • Example. Case of matching string type and its value(constant pattern match)
  • Example. Case with Boolean value(constant pattern match)
  • Example. Case with different datatypes(type pattern match)
  • Example. Case matching value and its datatype (pattern match)
  • Example. Case matching value and its datatype plus constraint on value (pattern match)
  • Example. Case matching value tuple (constant match)
  • Example. switch expression with lambda literal (pattern match)
  • Example. switch expression with lambda literal to match value tuple
  • Example. switch with delegate object, check if delegate exists
  • Example. switch with delegate invoked, (constant match)
  • Example. switch with delegate object (delegate type match)

Example. Case exact matching constant (constant pattern match)
class Demo
{
    public void Guess(int number)
    {
        switch (number)
        {
            case 1:
                Console.WriteLine("Value is 1");
                Console.WriteLine("Some more statements for 1.");
                break; // required
            case 2:
            case 3:
                Console.WriteLine("Value is 2 or 3");
                break; // required
            default:
                Console.WriteLine("Value did not match.");
                break; // required
        }
    }
}
class Program
{
    Random rand = new Random();
    static void Main(string[] args)
    {
        Program p = new();
        Demo demo = new();
        demo.Guess(p.rand.Next(1, 10));
    }
}

Example. Case of inexact match with relational operator(>,<,>=,<=) (constant pattern match)
class Demo
{
    public void Guess(int number)
    {
        switch (number)
        {
            case >= 10:
                Console.WriteLine("Greater than 10");
                break;

            case < 0:
                Console.WriteLine("Negative");
                break;

            case 5:
                Console.WriteLine("Exactly 5");
                break;
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Program p = new();
        Demo demo = new();
        demo.Guess(20);   // Greater than 10
        demo.Guess(-2);   // Negative
        demo.Guess(7);    // Not 5
        demo.Guess(5);    // Exactly 5
    }
}

Example. Case of matching with not operator(constant pattern match)
class Demo
{
    public void Guess(int number)
    {
        switch (number)
        {
            case not 5:
                Console.WriteLine("Other than 5");
                break;
            case 5:
                Console.WriteLine("Five");
                break;
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Program p = new();
        Demo demo = new();
        demo.Guess(20);   // Other than 5
        demo.Guess(5);    // Five
    }
}
Example. Case of matching string type and its value(constant pattern match)
class Demo
{
    public void Guess(string animal)
    {
        switch (animal)
        {
            case "Crow":
                Console.WriteLine("Crow is a bird");
                break; // required
            case "Cat":
            case "Dog":
                Console.WriteLine("Cat or Dog");
                break; // required
            default:
                Console.WriteLine("Not an animal.");
                break; // required
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Demo demo = new();
        demo.Guess("Dog");
        demo.Guess("Robert");
    }
} 
Example. Case with Boolean value(constant pattern match)
class Demo
{
    public void Guess(bool success)
    {
        switch (success)
        {
            case true:
                Console.WriteLine("Truth is life.");
                break; // required
            case false:
                Console.WriteLine("Life is hell for falsehood.");
                break; // required
            // default is optional, here not required
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Demo demo = new();
        demo.Guess(false);
        demo.Guess(true);
    }
}
Example. Case with different datatypes(type pattern match)
class Demo
{
    public void Guess(object data) // boxing
    {
        switch (data) // data should be primitive type
        {
            case int:
                Console.WriteLine("Integer type.");
                break; // required
            case float:
                Console.WriteLine("Float type.");
                break; // required
            case double:
                Console.WriteLine("Double type.");
                break; // required
            case bool:
                Console.WriteLine("Boolean type.");
                break; // required
                       // default is optional, here not required
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Demo demo = new();
        demo.Guess(11);
        demo.Guess(11.12F);
        demo.Guess(11.12);
        demo.Guess(true);
    }
}

Example. Case matching value and its datatype (pattern match)
class Demo
{
    public void Guess(object data) // boxing
    {
        switch (data) // data should be primitive type
        {
            case int x:
                Console.WriteLine($"Integer type value is {x}.");
                break; // required
            case float x:
                Console.WriteLine($"Float type value is {x}.");
                break; // required
            case double x:
                Console.WriteLine($"Double type value is {x}.");
                break; // required
            case bool x:
                Console.WriteLine($"Boolean type value is {x}.");
                break; // required
                       // default is optional, here not required
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Demo demo = new();
        demo.Guess(11);
        demo.Guess(11.12F);
        demo.Guess(11.12);
        demo.Guess(true);
    }
}
Example. Case matching value and its datatype plus constraint on value (pattern match)
class Demo
{
    public void Guess(object data) // boxing
    {
        switch (data) // data should be primitive type
        {
            case int x when x + 5 > 10:
                Console.WriteLine($"Integer type value is {x}.");
                break; // required
            case float x when x > 10.15F:
                Console.WriteLine($"Float type value is {x}.");
                break; // required
            case double x when x > 1.0:
                Console.WriteLine($"Double type value is {x}.");
                break; // required
            case bool x:
                Console.WriteLine($"Boolean type value is {x}.");
                break; // required
                       // default is optional, here not required
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Demo demo = new();
        demo.Guess(6);
        demo.Guess(1.12F);
        demo.Guess(1.12);
        demo.Guess(true);
    }
}

Example. Case matching value tuple (constant match)
class Demo
{
    public void Guess((int, int) data)
    {
        switch (data)
        {
            case (1, 1):
                Console.WriteLine($"Coordinate x and y are: {data.Item1} and {data.Item2}");
                break;
            case (2, 1):
                Console.WriteLine($"Coordinate x and y are: {data.Item1} and {data.Item2}");
                break;
            case (1, 2):
                Console.WriteLine($"Coordinate x and y are: {data.Item1} and {data.Item2}");
                break;
            default:
                Console.WriteLine("No tuple matched!");
                break;
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Demo demo = new();
        demo.Guess((1, 12));
        demo.Guess((1, 2));
    }
}

Example. switch expression with lambda literal (pattern match)
class Demo
{
    public void Guess(int input)
    {
        string result = input switch
        {
            1 => $"The output for 1 is {input + 10}",
            2 => $"The output for 2 is {input + 20}",
            _ => "No match found",
        };
        Console.WriteLine(result);
    }
}
class Program
{
    static void Main(string[] args)
    {
        Demo demo = new();
        demo.Guess(1);
        demo.Guess(2);
        demo.Guess(3);
    }
}
Example. switch expression with lambda literal to match value tuple
class Demo
{
    public void Guess((int, int) input)
    {
        string result = input switch
        {
            (1, 1) => $"{input.Item1} == {input.Item2}",
            (1, 2) => $"{input.Item1} < {input.Item2}",
            (2, 1) => $"{input.Item1} > {input.Item2}",
            _ => "No match found",
        };
        Console.WriteLine(result);
    }
}
class Program
{
    static void Main(string[] args)
    {
        Demo demo = new();
        demo.Guess((1, 1));
        demo.Guess((1, 2));
        demo.Guess((3, 2));
    }
}
Example. switch with delegate object, check if delegate exists
You can pass a delegate variable as the switch expression.

class Demo
{
    public void Test(Action d)
    {
        switch (d)
        {
            case null:
                Console.WriteLine("Delegate is null");
                break;

            default:
                Console.WriteLine("Delegate exists");
                break;
        }
    }
}
class Program
{
    Action someAction = () => Console.WriteLine("Hello");
    static void Main(string[] args)
    {
        Program p = new();
        Demo demo = new();
        demo.Test(p.someAction);
    }
}
Example. switch with delegate invoked, (constant match)
class Demo
{
    public void Test(Func<int> d)
    {
        switch (d())   // invoke delegate
        {
            case 10:
                Console.WriteLine("Ten");
                break;

            case > 10:
                Console.WriteLine("Greater than 10");
                break;
        }
    }
}
class Program
{
    Func<int> someDelegate = () => 10;
    static void Main(string[] args)
    {
        Program p = new();
        Demo demo = new();
        demo.Test(p.someDelegate);
    }
}
Example. switch with delegate object (delegate type match)
class Demo
{
    public void Test(Delegate d)
    {
        switch (d)
        {
            case Action:
                Console.WriteLine("Action delegate");
                break;

            case Func<int>:
                Console.WriteLine("Func<int>");
                break;
            default:
                break;
        }
    }
}
class Program
{
    Delegate d = (Action)(() => { });
    static void Main(string[] args)
    {
        Program p = new();
        Demo demo = new();
        demo.Test(p.d);
    }
}
==

C# Nullable reference types and Operators on Nullable Types

In this post we learn about:
  • Nullable reference types
  • Non-nullable reference types
Nullable reference types: They may have null value and they are declared similar to nullable value types by using nullable annotation(?) after data type.
Example
string? FirstName;
They must be initialized before using it.
string? FirstName = null;

Non-nullable reference types: They must be assigned a non-null value at initialization and cannot be later changed to a null value.
Example
string Name = String.Empty;

Enabling Nullable reference types Feature in Visual Studio
You can enable/disable nullable reference type features in project file using enable/disable/warning values. The current settings uses enable value:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
  </PropertyGroup>

</Project>

Operating on Nullable Types
C# provides several operators for working with nullable types. They are
  • the null-coalescing operator, 
  • the null-coalescing assignment operator, and 
  • the null conditional operator.

The null-coalescing operator
Look at the following code. The null-coalescing operator(??) returns the second operand if first operand is null else it returns the first operand.

string? FirstName = null;
var result = FirstName ?? ThisValueIfFirstNameIsNull;

Example of traditional approach would be:
class Test
{
    public string? FirstName = null;
    string result = string.Empty;
    public string GetName()
    {
        if (FirstName != null)
        {
            result = FirstName;
        }
        else
        {
            result = "ThisValueIfFirstNameIsNull";
        }
        return result;
    }
}
class Program
{
    static void Main(string[] args)
    {
        Test test = new Test();
        test.FirstName = "Ajeet";
        Console.WriteLine(test.GetName());
        test.FirstName = null;
        Console.WriteLine(test.GetName());
    }
}
The  null-coalescing operator(??) provides concise solution for the same:
class Test
{
    public string? FirstName = null;
    string result = string.Empty;
    public string GetName()
    {
        return FirstName ?? "ThisValueIfFirstNameIsNull";
    }
}
class Program
{
    static void Main(string[] args)
    {
        Test test = new Test();
        test.FirstName = "Ajeet";
        Console.WriteLine(test.GetName());
        test.FirstName = null;
        Console.WriteLine(test.GetName());
    }
}
The null-coalescing assignment operator
Look at the following code. The null-coalescing assignment operator(??) assigns the second operand to first one if first operand is null and then returns else it simply returns the first operand.

string? FirstName = null;
FirstName ??= ThisValueIfFirstNameIsNull;

Example of traditional Approach would be:
class Test
{
    public string? FirstName = null;
    public string GetName()
    {
        if (FirstName != null)
        {
            return FirstName;
        }
        else
        {
            return "ThisValueIfFirstNameIsNull";
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Test test = new Test();
        test.FirstName = "Ajeet";
        Console.WriteLine(test.GetName());
        test.FirstName = null;
        Console.WriteLine(test.GetName());
    }
}
The  null-coalescing assignment operator(??=) provides concise solution for the same:
class Test
{
    public string? FirstName = null;
    public string GetName()
    {
        return FirstName ??= "ThisValueIfFirstNameIsNull";
    }
}
class Program
{
    static void Main(string[] args)
    {
        Test test = new Test();
        test.FirstName = "Ajeet";
        Console.WriteLine(test.GetName());
        test.FirstName = null;
        Console.WriteLine(test.GetName());
    }
}
The null-coalescing assignment operator
The null conditional operator(?.) evaluates the expression if first expression is not null else it ignores the expression:

expression1?.expression2
  • The expression2 is generally a property or method of an object.
  • The null conditional operator(?.) helps to avoid null reference exception at runtime.
Example
class Test
{
    public int? GetLength(string? name)
    {
        return name?.Length; // possiibility of NullReferenceException without ?.
    }
}
class Program
{
    string? first = null;
    string? last = "Kumar";
    int? result;
    static void Main(string[] args)
    {
        Program obj = new Program();
        Test test = new Test();
        obj.result = test.GetLength(obj.first);
        obj.result = test.GetLength(obj.last);
        Console.WriteLine(obj.result);
    }
}

Hot Topics