Modern C# · Lesson 15

Record copies and shared collections

Inspect equality and mutation when records contain reference members.

How it works

A record's generated equality is useful for data values, but its members still use their own equality rules. Playlist contains a List reference. Creating another list with the same initial text does not make that list equal by content. The generated record comparison does not recursively invent sequence equality for every collection.

The with expression creates a new Playlist instance while copying its member values. The copied Tracks value is a reference to the original list. Adding a second track through copy therefore changes the list seen by first as well. The printed count is two even though first itself was never reassigned.

The first equality comparison is true because both records contain the same list reference. The separate record contains a different list reference, so its comparison is false. SequenceEqual explicitly asks a different question: whether two sequences contain matching elements in the same order. Its successful result concerns the track contents, not the playlist object identities.

A common mistake is to treat the word record as an automatic deep immutability guarantee. Generated copying and equality cannot choose the right policy for an arbitrary object graph. Some applications intentionally share a mutable collection; others need a snapshot. That choice belongs in the model's construction and update rules.

Practice by building a revised playlist with Tracks = first.Tracks.ToList() in the with initializer. Add another track only to the revised list, then print both counts and compare the records. Explain why separating mutation does not by itself give collection properties value-based equality.

For a larger design, decide whether callers should receive a mutable list at all. A private copied collection with a read-only public view can provide a clearer boundary. Test both ownership and comparison separately: one test should detect accidental shared mutation, while another should state the equality rule the product actually needs.

A working example

using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

var first = new Playlist(new List<string> { "one" });
var copy = first with { };
var separate = new Playlist(new List<string> { "one" });
copy.Tracks.Add("two");
Console.WriteLine(first.Tracks.Count);
Console.WriteLine(first == copy);
Console.WriteLine(first == separate);
Console.WriteLine(first.Tracks.SequenceEqual(new[] { "one", "two" }));
record Playlist(List<string> Tracks);

Expected output

2
True
False
True
Try this
A with expression copies the reference stored in Tracks.