rgb
rgb

Reputation: 1860

Changing property value in nested records

Is it possible to use the with keyword to create a new instance of nested records with a different value for the nested property - both cases: simple property and collection? Let's see an example:

class Program
{
    static void Main(string[] args)
    {
        var company = new Company(
            Name: "Company1",
            Branch: new Branch(
                Location: "Krakow",
                Employees: new[]
                {
                    new Employee("Robert")
                }));

        Console.WriteLine(company);
    }
}

internal record Company(string Name, Branch Branch);
internal record Branch(string Location, IEnumerable<Employee> Employees);
internal record Employee(string FirstName);

In the above example I want to create a new record, but with changed values of the branch location ("Krakow") and employee name ("Robert"). How can I do this most effectively?

Upvotes: 7

Views: 4317

Answers (2)

Daniel Slapman
Daniel Slapman

Reputation: 106

You can use LeviySoft.Visor to avoid awkwardness of nested withs:

[Optics(withNested: true)]
internal partial record Company(string Name, Branch Branch);
[Optics(withNested: true)]
internal partial record Branch(string Location, IEnumerable<Employee> Employees);
[Optics]
internal partial record Employee(string FirstName);

var original = new Company(...)

var updated = Company.FocusBranch.LocationLens.Set("Warshaw")(original)

As for the IEnumerable's - you can implement your own IProperty for the IEnumerables taking this as example. Please note that using non-immutable collection interfaces breaks by-value comparison of records (it's the reason why I did not implemented IProperty for IEnumerable)

Upvotes: 0

CodeCaster
CodeCaster

Reputation: 151690

You can nest your with expressions:

var clone = company with {
    Name = "Company2",
    Branch = company.Branch with {
        Location = "Warshaw",
        Employees = new[]
        {
            company.Branch.Employees.First() with 
            {
                FirstName = "Bob"
            }
        }}};

Console.WriteLine(clone);
foreach (var e in clone.Branch.Employees)
{
    Console.WriteLine(e);
}

Upvotes: 10

Related Questions