简体   繁体   中英

What is the name of this syntax in C# (multiple tuple-like assignment)?

I have found this syntax in MS docs:

public NewsStoryViewModel(DateTimeOffset published, string title, string uri) =>
        (Published, Title, Uri) = (published, title, uri);

It does multiple assignment in one statement, but I am not quite sure what it's called or how it works.

Is this some kind of a trick do multiple assignment by pretending these are tuples or does it have it's own name?

PS It is mentioned here if anyone is interested

PPS It's not the absence of {} I am wondering about, but rather line 2 with a "fancy" assignment instead of a traditional one.

It's a tuple that is deconstructed to class properties.

public NewsStoryViewModel(DateTimeOffset published, string title, string uri) {
    // tuple
    var myTuple = (published, title, uri);
    // deconstruction
    (Published, Title, Uri) = myTuple;
}

Deconstruction works for tuples out of the box. See https://docs.microsoft.com/en-us/dotnet/csharp/tuples#deconstruction for details.

I don't know if they have official terminology for doing this in a constructor specifically, it's called deconstruction as a general pattern. As long as the right-hand-side of assignment has a suitable Deconstruct method, you can extract values from a type perform assignment in bulk like this. For tuples, this:

var (x, y, z) = (1, 2, 3);

is morally equivalent to

var tuple = (1, 2, 3);
var x = tuple.Item1;
var y = tuple.Item2;
var z = tuple.Item3;

The left-hand-side can have any assignable variables, they can be locals, fields or get-only properties in a constructor. Using that is personal preference and code-style, I usually use them as in the MSDN docs - to write basic ctors in one line:

class C
{
    private int Prop1 { get; }
    private D Prop2 { get; }
    private string Prop3 { get; }

    public C(int prop1, D prop2, string prop3) =>
        (Prop1, Prop2, Prop3) = (prop1, prop2, prop3);
}

An interesting fact is that the Roslyn compiler recognises this pattern and avoids actually creating a tuple. If you throw this into a decompiler, you'll see the generated code is the same as for:

public C(int prop1, D prop2, string prop3)
{
    Prop1 = prop1;
    Prop2 = prop2;
    Prop3 = prop3;
}

I don't know if it's documented anywhere, but credit to Jon Skeet who mentioned this in his C# in Depth (4th ed.).

Update: Out of curiosity I checked if the optimisation applies in other places as well. It seems that it works as long as the left-hand-side variables are ref or out parameters. For example this:

public void Deconstruct(out int prop1, out object prop2, out string prop3) =>
    (prop1, prop2, prop3) = (_prop1, _prop2, _prop3);

generates code equivalent to this:

public void Deconstruct(out int prop1, out object prop2, out string prop3)
{
    int temp1 = _prop1;
    object temp2 = _prop2;
    string temp3 = _prop3;
    prop1 = temp1;
    prop2 = temp2;
    prop3 = temp3;
}

My educated guess is that since the IL differs between assignments to "normal" variables and to ref variables, the optimisation is simply not implemented for the second case, but I might be wrong.

The second line basically acts as a setter for properties. Prior to C# 7.0, one can do:

public NewsStoryViewModel(DateTimeOffset published, string title, string uri) 
{
        Published = published;
        Title = title; 
        Uri = uri;
}

C# 7.0:

public NewsStoryViewModel(DateTimeOffset published, string title, string uri) =>
        (Published, Title, Uri) = (published, title, uri);

As Published, Title and Uri are already defined, one can assign them directly like above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM