Destructuring assignment - object properties to variables in C#
Asked Answered
N

4

55

JavaScript has a nifty feature where you can assign several variables from properties in an object using one concise line. It's called destructuring assignment syntax which was added in ES6.

// New object
var o = {p1:'foo', p2:'bar', p3: 'baz'};
// Destructure
var {p1, p2} = o;
// Use the variables...
console.log(p1.toUpperCase()); // FOO
console.log(p2.toUpperCase()); // BAR

I want to do something similar with C#.

// New anonymous object
var o = new {p1="foo", p2="bar", p3="baz"};
// Destructure (wrong syntax as of C#6)
var {p1, p2} = o;
// Use the variables...
Console.WriteLine(p1.ToUpper()); // FOO
Console.WriteLine(p2.ToUpper()); // BAR

Is there a syntax to do this in C#?

Nester answered 2/3, 2016 at 17:22 Comment(2)
what scenario required this feature, just curious!Dirt
@Dirt There is no requirement for this language feature (or any language feature for that matter). It's all syntactic sugar on top of zeros and ones :) In this case, I want to write less code and this is a feature that enables it.Nester
S
8

The positional syntax for records comes with deconstruction by default (dotnet fiddle):

public record Person(string firstName, string lastName) {}

var person = new Person("Kyle", "Mit");
var (firstName, lastName) = person;
Console.WriteLine(firstName); // "Kyle"

Looking at the generated code via SharpLab, it just implements a regular Deconstruct method that you could add to your own type if you weren't using records:

[CompilerGenerated]
public void Deconstruct(out string firstName, out string lastName)
{
    firstName = this.firstName;
    lastName = this.lastName;
}

According to the docs on Deconstruction

C# doesn't offer built-in support for deconstructing non-tuple types other than the record and DictionaryEntry types. However, as the author of a class, a struct, or an interface, you can allow instances of the type to be deconstructed by implementing one or more Deconstruct methods.

Saltant answered 4/1, 2023 at 17:0 Comment(1)
Thanks! I particularly like that you can add public void Deconstruct to any class so this best answers the question! (the dotnet fiddle was a nice too so I could confirm it works)Nester
C
36

Closest thing which could help you are Tuples.

C#7 maybe will have something like this:

public (int sum, int count) Tally(IEnumerable<int> values) 
{
    var res = (sum: 0, count: 0); // infer tuple type from names and values
    foreach (var value in values) { res.sum += value; res.count++; }
    return res;
}


(var sum, var count) = Tally(myValues); // deconstruct result
Console.WriteLine($"Sum: {sum}, count: {count}"); 

Link to discussion

Right now it is not possible.

Chaco answered 2/3, 2016 at 17:31 Comment(4)
See also the proposed featureVial
See updated link to the proposed featureNester
Yup, C# 7.0 supports deconstruction: blogs.msdn.microsoft.com/dotnet/2016/08/24/…Endue
Tuples are the correct way to do this now that C#7 is here. In fact, I would even say that going forward, you should completely replace anonymous types with tuples because of the more elegant syntax. If your type has more properties than tuples support, you should be making a quick little class anyway.Olivia
A
28

C# 7 Using Tuples. You can achieve something like this.

You can construct and destruct a Tuple. Snippet

var payLoad = (
    Username: "MHamzaRajput",
    Password: "password",
    Domain: "www.xyz.com",
    Age: "24" 
);

// Hint: You just need to follow the sequence. 

var (Username, Password, Domain, Age) = payLoad;
// or
var (username, password, _, _) = payLoad;

Console.WriteLine($"Username: {username} and Password: {password}"); 

Output

Username: MHamzaRajput and Password: password
Avidin answered 8/4, 2020 at 9:10 Comment(2)
I get error CS0128: A local variable named _ is already defined in this scope. Do you need two different names for the underscores?Nester
I have uploaded a snippet link you can test there.Avidin
S
8

The positional syntax for records comes with deconstruction by default (dotnet fiddle):

public record Person(string firstName, string lastName) {}

var person = new Person("Kyle", "Mit");
var (firstName, lastName) = person;
Console.WriteLine(firstName); // "Kyle"

Looking at the generated code via SharpLab, it just implements a regular Deconstruct method that you could add to your own type if you weren't using records:

[CompilerGenerated]
public void Deconstruct(out string firstName, out string lastName)
{
    firstName = this.firstName;
    lastName = this.lastName;
}

According to the docs on Deconstruction

C# doesn't offer built-in support for deconstructing non-tuple types other than the record and DictionaryEntry types. However, as the author of a class, a struct, or an interface, you can allow instances of the type to be deconstructed by implementing one or more Deconstruct methods.

Saltant answered 4/1, 2023 at 17:0 Comment(1)
Thanks! I particularly like that you can add public void Deconstruct to any class so this best answers the question! (the dotnet fiddle was a nice too so I could confirm it works)Nester
D
3

I've been looking for something like this for a while now and I came across Deconstructor with Classes. This isn't as elegant of a solution as Javascript gives us, but it can definitely simplify a lot of things if done well.

Below is a small example (I have not run this code). I hope this helps others:

class Animal
{
    public string name;
    public string type;

    public Animal(
            string name, 
            string type
        )
    {
        this.name = name;
        this.type = type;
    }

    // Use out params with a 
    // method called Deconstruct
    public void Deconstruct(
            out string name, 
            out string type
        )
    {
        name = this.name;
        type = this.type;
    }
}

class Shelter
{
    public Animal[] animals;
    public int animalCapacity;
    public int numberOfAnimalsInShelter = 0;

    public Shelter(
            int animalCapacity
        )
    {
        this.animalCapacity = animalCapacity;
        animals = new Animal[animalCapacity];
    }

    public void AddAnimalToShelter(
            Animal animal
        )
    {
        animals[numberOfAnimalsInShelter] = animal;
        numberOfAnimalsInShelter++;
    }

    public void AnimalsInShelter()
    {
        for (int i = 0; i < animals.Length; i++)
            {
            // Here is how to use the Deconstructor 
            // method from the Animal class
                var (name, type) = animals[i];
                Console.WriteLine(name);
                Console.WriteLine(type);
            }
    }
}

Output should be the name and type for every animal that gets added to the shelter.

Demisemiquaver answered 1/8, 2022 at 16:55 Comment(1)
Records does have seamless Deconstruct learn.microsoft.com/en-us/dotnet/csharp/language-reference/…While

© 2022 - 2024 — McMap. All rights reserved.