Search code examples
c#structtuplesinlinec#-7.0

Is there a way to inline tuple deconstruction to avoid an unnecessary allocation?


I have the following example struct:

struct Data {
    internal long a;
    internal long b;

    internal void Deconstruct(out long aa, out long bb) {
        aa = a; bb = b;
    }
}

What if I want only to use the values of the struct an forget about the struct itself?

Data Generate()
    => new Data() { a = 3, b = 5 };

void Test() {
    (var a, var b) = Generate();
    Console.WriteLine(a);
    Console.WriteLine(b);
}

The call to Generate creates a struct and it is decomposed immediately in its parts. Can I somehow inline this process and get rid of the struct altogether?

I compiled (this class library) with VS 15.5.7 in release mode an ilspy is showing:

.method private hidebysig 
    instance void Test () cil managed 
{
    // Method begins at RVA 0x208c
    // Code size 33 (0x21)
    .maxstack 3
    .locals init (
        [0] int64,
        [1] valuetype StackOverflowDemo.Q1/Data,
        [2] int64,
        [3] int64
    )

    IL_0000: ldarg.0
    IL_0001: call instance valuetype StackOverflowDemo.Q1/Data StackOverflowDemo.Q1::Generate()
    IL_0006: stloc.1
    IL_0007: ldloca.s 1
    IL_0009: ldloca.s 2
    IL_000b: ldloca.s 3
    IL_000d: call instance void StackOverflowDemo.Q1/Data::Deconstruct(int64&, int64&)
    IL_0012: ldloc.2
    IL_0013: ldloc.3
    IL_0014: stloc.0
    IL_0015: call void [System.Console]System.Console::WriteLine(int64)
    IL_001a: ldloc.0
    IL_001b: call void [System.Console]System.Console::WriteLine(int64)
    IL_0020: ret
} // end of method Q1::Test

Solution

  • If you want to forget the struct, instead of

    Data Generate()
        => new Data() { a = 3, b = 5 };
    

    Why not use

    void Generate(out long aa, out long bb)
    {
        aa = 3; bb = 5;
    }
    

    That eliminates the struct as you asked.

    If you want to keep the struct but get rid of the extra allocation, you could also do this:

    class Data {
        public long A { get; internal set; }
        public long B { get; internal set; }
    
        internal Data(long a, long b) {
            A = a; B = b;
        }
    }
    

    Then use:

    void Test() {
        var data = Generate();
        Console.WriteLine(data.A);
        Console.WriteLine(data.B);
    }