DEV Community

loading...

[C#] Play with switch

masanori_msl profile image Masui Masanori ・5 min read

Intro

In C# 7 and C# 8, "switch" got some new features.
In this time, I will try using "switch".

Environments

  • .NET 5.0.100

Before C# 7 (Constant Patterns)

Program.cs

class Program
{
    static void Main(string[] args)
    {
        int id = 1;
        switch(id)
        {
            case 0:
                Console.WriteLine("Id was 0");
                break;
            case 1:
                Console.WriteLine("Id was 1");
                break;
            default:
                Console.WriteLine("Not found");
                break;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Only constant values can be used.

New Patterns

Type Patterns

After C# 7, I can use type.
In type pattern, after checking if a target object can be converted, converted instance will be created.

RecordSample.cs

    public record RecordSample(int Id, string Name);
Enter fullscreen mode Exit fullscreen mode

Program.cs

    static void Main(string[] args)
    {
        RecordSample r = new (3, "Hello"); 
        Play((object)r);
    }
    private static void Play(object target)
    {
        switch(target)
        {
            case RecordSample r:
                Console.WriteLine($"Record Id: {r.Id} Name: {r.Name}");
                break;
            default:
                Console.WriteLine("Not found");
                break;
        }
    }
Enter fullscreen mode Exit fullscreen mode

Result

Record Id: 3 Name: Hello
Enter fullscreen mode Exit fullscreen mode

Tuple Patterns

Program.cs

        static void Main(string[] args)
        {
            (int Id, string Name) t = (0, "Hello");
            Play((object)t);
        }
        private static void Play(object target)
        {
            switch(target)
            {
                case RecordSample r:
                    Console.WriteLine($"Record Id: {r.Id} Name: {r.Name}");
                    break;
                case (0, "Hello"):
                    Console.WriteLine("Tuple");
                    break;
                default:
                    Console.WriteLine("Not found");
                    break;
            }
        }
Enter fullscreen mode Exit fullscreen mode

Result

Tuple
Enter fullscreen mode Exit fullscreen mode

Property Patterns

Program.cs

        static void Main(string[] args)
        {
            var r = new RecordSample(1, "Hello");
            Play((object)r);
        }
        private static void Play(object target)
        {
            switch(target)
            {
                case RecordSample { Id: 1} r:
                    Console.WriteLine($"Property pattern Id: {r.Id} Name: {r.Name}");
                    break;
                case RecordSample r:
                    Console.WriteLine($"Record Id: {r.Id} Name: {r.Name}");
                    break;
                default:
                    Console.WriteLine("Not found");
                    break;
            }
        }
Enter fullscreen mode Exit fullscreen mode

Result

Property pattern Id: 1 Name: Hello
Enter fullscreen mode Exit fullscreen mode

I also can use some more patterns(ex. Positional Patterns).

when

In new Patterns(ex. Type Patterns), I can separate into more specific conditions.

Program.cs

        static void Main(string[] args)
        {
            var r = new RecordSample(1, "Hello");
            Play((object)r);
        }
        private static void Play(object target)
        {
            switch(target)
            {
                case RecordSample r when r.Id < 0:
                    Console.WriteLine($"Record1 Id: {r.Id} Name: {r.Name}");
                    break;
                case RecordSample r when r.Id >= 1:
                    Console.WriteLine($"Record2 Id: {r.Id} Name: {r.Name}");
                    break;
                case RecordSample r:
                    Console.WriteLine($"Record Id: {r.Id} Name: {r.Name}");
                    break;
                default:
                    Console.WriteLine("Not found");
                    break;
            }
        }
Enter fullscreen mode Exit fullscreen mode

Result

Record2 Id: 1 Name: Hello
Enter fullscreen mode Exit fullscreen mode

switch expression

In C# 8, "switch expression" was added.

I can refactor like below.

Before

Program.cs

        static void Main(string[] args)
        {
            int id = 1;
            string message = GetMessage(id);
            Console.WriteLine(message);
        }
        private static string GetMessage(int id)
        {
            switch(id)
            {
                case 0:
                    return "ID was 0";
                case 1:
                    return "ID was 1";
                default:
                    return "Other";
            }
        }
Enter fullscreen mode Exit fullscreen mode

Result

ID was 1
Enter fullscreen mode Exit fullscreen mode

After

Program.cs

        static void Main(string[] args)
        {
            int id = 1;
            string message = id switch
            {
                0 => "ID was 0",
                1 => "ID was 1",
                _ => "Other"
            };
            Console.WriteLine(message);
        }
Enter fullscreen mode Exit fullscreen mode

Result

ID was 1
Enter fullscreen mode Exit fullscreen mode

I can use same patterns as the switch statement.

Resources

How many instances are created?

Question

For example, when I execute this code, how many instances are created?

Program.cs

        static void Main(string[] args)
        {
            var r = new RecordSample(0, "Hello");
            Play((object)r);
        }
        private static void Play(object target)
        {
            switch(target)
            {
                case RecordSample r when r.Id < 0:
                    Console.WriteLine($"Record1 Id: {r.Id} Name: {r.Name}");
                    break;
                case RecordSample r when r.Id >= 1:
                    Console.WriteLine($"Record2 Id: {r.Id} Name: {r.Name}");
                    break;
                case RecordSample r:
                    // Execute here
                    Console.WriteLine($"Record Id: {r.Id} Name: {r.Name}");
                    break;
                default:
                    Console.WriteLine("Not found");
                    break;
            }
        }
Enter fullscreen mode Exit fullscreen mode

Answer

Only one instance is created.

Decompile

I decompile this code by ILSpy(https://marketplace.visualstudio.com/items?itemName=icsharpcode.ilspy-vscode).

private static void Play(object target)
{
    RecordSample recordSample = target as RecordSample;
    if ((object)recordSample != null)
    {
        if (recordSample.Id < 0)
        {
            Console.WriteLine($"Record1 Id: {recordSample.Id} Name: {recordSample.Name}");
            return;
        }
        RecordSample recordSample2 = recordSample;
        if (recordSample2.Id >= 1)
        {
            Console.WriteLine($"Record2 Id: {recordSample2.Id} Name: {recordSample2.Name}");
            return;
        }
        RecordSample recordSample3 = recordSample;
        Console.WriteLine($"Record Id: {recordSample3.Id} Name: {recordSample3.Name}");
    }
    else
    {
        Console.WriteLine("Not found");
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. A instance is created by "as RecordSample"
  2. If it isn't null, use the if statement to perform the operation under the condition of the when clause.

So only one instance is created.

Convert an object like TypeScript

In TypeScript, I can convert if an instance has same property as RecordSample class.

record-sample.ts

export class RecordSample {
    public id: number = -1;
    public name: string = "";
}
Enter fullscreen mode Exit fullscreen mode

program.ts

import { RecordSample } from "./record-sample";

export function main(){
    const s = {
        id: 1,
        name: "Hello"
    };
    const r: RecordSample = s; // <- OK
}
Enter fullscreen mode Exit fullscreen mode

I also want to convert like this in C#.

First, I add a method to convert.

RecordSample.cs

    public record RecordSample
    {
        public int Id { get; init; }
        public string Name { get; init; } = "";
        public RecordSample(int id, string name)
        {
            Id = id;
            Name = name;
        }
        public static bool TryParse(object original, out RecordSample? result)
        {
            result = null;
            var type = original.GetType();
            var idValue = type.GetProperty("Id")?.GetValue(original)?.ToString();
            var nameValue = type.GetProperty("Name")?.GetValue(original)?.ToString();
            if(idValue == null || nameValue == null)
            {
                return false;
            }
            if(int.TryParse(idValue.ToString(), out var id) == false)
            {
                return false;
            }
            result = new RecordSample(id, nameValue);
            return true;
        }
    }
Enter fullscreen mode Exit fullscreen mode

And I add this into the switch statement.

        private static void Play(object target)
        {
            switch(target)
            {
                case RecordSample r:
                    Console.WriteLine($"Record Id: {r.Id} Name: {r.Name}");
                    break;
                case var o when RecordSample.TryParse(o, out var r):
                    Console.WriteLine($"Property Id: {r!.Id} Name: {r!.Name}");
                    break;
                default:
                    Console.WriteLine("Not found");
                    break;
            }
        }
Enter fullscreen mode Exit fullscreen mode

"var" in switch statements can catch all conditions.
And check target's properties by when clause.

Program.cs

        static void Main(string[] args)
        {
            var r = new {
                Id = 0,
                Name = "Hello"
            };
            Play((object)r);
        }
        private static void Play(object target)
        {
            switch(target)
            {
                case RecordSample r:
                    Console.WriteLine($"Record Id: {r.Id} Name: {r.Name}");
                    break;
                case var o when RecordSample.TryParse(o, out var r):
                    Console.WriteLine($"Property Id: {r!.Id} Name: {r!.Name}");
                    break;
                default:
                    Console.WriteLine("Not found");
                    break;
            }
        }
Enter fullscreen mode Exit fullscreen mode

Result

Property Id: 0 Name: Hello
Enter fullscreen mode Exit fullscreen mode

Discussion

pic
Editor guide