Skip to content

Pattern Matching: Multiple property reads #34933

@HaloFour

Description

@HaloFour

I understand that the number of property reads to facilitate pattern matching is undefined so that the compiler can optimize for as few reads as it can. I was curious about the following case as the compiler emits two reads for the property Name although the compiler should be able to deduce that both are for the same property on the Person class and could theoretically optimize it down to one read:

using System;

public class Person {
    public string Name { get; set; }
}

public class Student : Person { }


public class C {
    public void M(Person p) {
        switch (p) {
            case { Name: "Bill" }:
                Console.WriteLine("Hey Bill!");
                break;
            case Student { Name: var name }:
                Console.WriteLine($"Hello student {name}!");
                break;
            case { Name: var name }:
                Console.WriteLine($"Hello non-student {name}!");
                break;
        }
    }
}

This results in IL that accesses Name twice, once for the first case and once for the second case. I assume that this is due to the type-switch perhaps interfering with internal bookkeeping and I can't think of a reason why it would be "Correct"™ to emit the two reads.

Decompilation of the resulting IL:

public class C
{
    public void M(Person p)
    {
        if (p != null)
        {
            string name = p.Name; // read #1
            if (name == null || !(name == "Bill"))
            {
                Student student = p as Student;
                if (student != null)
                {
                    string name2 = student.Name; // read #2
                    Console.WriteLine("Hello student " + name2 + "!");
                }
                else
                {
                    Console.WriteLine("Hello non-student " + name + "!");
                }
            }
            else
            {
                Console.WriteLine("Hey Bill!");
            }
        }
    }
}

This would make sense if Student shadowed Name, but it doesn't and the compiler is aware of that as it emits callvirt calls to Person::get_Name both times. If Name was virtual it would still only necessitate a single call.

Any particular reason for this, or is it just a case that the compiler hasn't optimized for?

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions