Ignore private member variables when doing equality checks with records in C#

Viewed 712

I have a record definition where I would like to only check equality on public/specific member variables in the record definition. I haven't found a way to do this without making a custom Equals function and I would prefer not to that if there is another nicer solution. Any reflections on whether private member variables in records is a bad pattern or not, is also appreciated.

Example of record where equality is true:

        public record Test
        {
            public string Variable { get; init; }

            public Test(string someValue)
            {
                Variable = someValue;
            }
        }

        [Fact]
        public void RecordTest()
        {
            var test1 = new Test("hello");
            var test2 = new Test("hello");

            Assert.Equal(test1, test2); // Passes
        }

Example of record where I would like it to be true, but it's not:

       public record Test
        {
            // I believe this is causing it to fail, can it be ignored somehow?
            private readonly List<string> _list = new();

            public string Variable { get; init; }

            public Test(string someValue)
            {
                Variable = someValue;
                _list.Add(someValue);
            }
        }

        [Fact]
        public void RecordTest()
        {
            var test1 = new Test("hello");
            var test2 = new Test("hello");

            Assert.Equal(test1, test2); // Fails
        }
2 Answers

In the microsoft documentation you can read this:

For records, value equality means that two variables of a record type are equal if the types match and all property and field values match. For other reference types such as classes, equality means reference equality. That is, two variables of a class type are equal if they refer to the same object. Methods and operators that determine equality of two record instances use value equality.

The list here needs to have the same reference for the equality to work. I think using an equality comparer or override equal is the best way to go.

What about creating your own class that wraps your list, and implement your own object Equals and GetHashCode overrides, as well as implementing IEquatable interface. Then use your own wrapper in the record. Something like:

public class ListWrapper : IEquatable<ListWrapper>
{
    private readonly List<string> _list = new();

    public void Add(string item) => _list.Add(item);

    public bool Equals(ListWrapper other)
    {
        return _list.SequenceEqual(other._list);
    }

    // you may or may not want to override object Equals and GetHashCode.
}


public record Test
{
    private readonly ListWrapper _list = new();

    public string Variable { get; init; }

    public Test(string someValue)
    {
        Variable = someValue;
        _list.Add(someValue);
    }
}
Related