Skip to content

Unexpected behavior of property increment when receiver is a generic type parameter substituted with a reference type #79268

Closed
@AlekseyTs

Description

@AlekseyTs
interface I1
{
    public int P1 {get; set;}
}

class C1 : I1
{
    public int F1;

    public int P1
    {
        get
        {
            System.Console.Write(F1);
            Program.F = new C1 { F1 = Program.F.F1 + 1 };
            return 0;
        }
        set
        {
            System.Console.Write(F1);
        }
    }
}

class Program
{
    public static C1 F = new C1 { F1 = 123 };

    static void Main()
    {
        Test1(ref F);
        System.Console.Write(F.F1);

        System.Console.WriteLine();
        System.Console.WriteLine("----------");
        
        F = new C1 { F1 = 123 };
        Test2(ref F);
        System.Console.Write(F.F1);
    }

    static void Test1<T>(ref T f) where T : I1
    {
        f.P1++;
    }

    static void Test2(ref C1 f)
    {
        f.P1++;
    }
}

Observed:

123124124
----------
123123124

Property setter prints "124" for generic scenario (it is the number in the middle of the first line in the output). Which means that getter and setter are executed on a different instances, but the receiver is supposed to be the same for both.

It is clear from IL why this is happening (the receiver is captured by reference and is used for constrained. calls):

    .method private hidebysig static 
        void Test1<(I1) T> (
            !!T& f
        ) cil managed 
    {
        // Method begins at RVA 0x2128
        // Code size 31 (0x1f)
        .maxstack 3
        .locals init (
            [0] int32
        )

        IL_0000: nop
        IL_0001: ldarg.0
        IL_0002: dup
        IL_0003: constrained. !!T
        IL_0009: callvirt instance int32 I1::get_P1()
        IL_000e: stloc.0
        IL_000f: ldloc.0
        IL_0010: ldc.i4.1
        IL_0011: add
        IL_0012: constrained. !!T
        IL_0018: callvirt instance void I1::set_P1(int32)
        IL_001d: nop
        IL_001e: ret
    } // end of method Program::Test1

Expected:

123123124
----------
123123124

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions