Home > Blockchain >  Comparing record types with LINQ in C#
Comparing record types with LINQ in C#

Time:03-17

I am trying to implement record types to make comparing less complex. But I ran into issues comparing records to each other. I expect records to be equal when properties are equal to each other but this isn't the case in my code..

I have a record class called MyAlarm with some properties. It inherits from the interface IAlarm which is empty.

public record MyAlarm : IAlarm
{
    public int Prop1 { get; init; }
    public int Prop2 { get; init; }
}

A class called VisibleAlarm has the IAlarm as property plus some extra data.

public record VisibleAlarm
{
    public IAlarm InternalAlarm { get; init; }
    public string Message { get; set; }
}

In a class called AlarmService the VisibleAlarms are listed and updated when needed. This service has a List of visible alarms.

Further in this alarm service the list is checked if an alarm already exists depending on the IAlarm property. I have tried two methods:

public class AlarmService
{
    private List<VisibleAlarm> _alarms = new();

    // ... Other code that handles alarm stuff

    private VisibleAlarm GetExistingAlarm1(IAlarm alarm)
    {
        return _alarms.FirstOrDefault(a => a.InternalAlarm == alarm);
    }

    private VisibleAlarm GetExistingAlarm2(IAlarm alarm)
    {
        foreach (var existingAlarm in _alarms)
        {
            if (existingAlarm.InternalAlarm == alarm)
            {
                return existingAlarm;
            }
        }
    }
}

When debugging the code I see that the record properties of the existingAlarm.InternalAlarm and the given alarm match but still the GetExistingAlarm methods return both false. Am i missing something or must I implement a IEqualityComparer because of interface IAlarm?

Cheers

CodePudding user response:

In your line of code:

return _alarms.FirstOrDefault(a => a.InternalAlarm == alarm);

the a.InternalAlarm == alarm is calling operator== rather than object.Equals() to do the comparison.

You can fix this by changing the code to:

return _alarms.FirstOrDefault(a => a.InternalAlarm.Equals(alarm));

The following program demonstrates the difference:

using System;

static class Program
{
    public static void Main()
    {
        MyAlarm ma1 = new MyAlarm { Prop1 = 1, Prop2 = 2 };
        MyAlarm ma2 = new MyAlarm { Prop1 = 1, Prop2 = 2 };
        
        VisibleAlarm va1 = new VisibleAlarm { InternalAlarm = ma1, Message = "message" };
        VisibleAlarm va2 = new VisibleAlarm { InternalAlarm = ma2, Message = "message" };

        Console.WriteLine(ma1 == ma2); // True
        Console.WriteLine(va1 == va2); // True
        Console.WriteLine(va1.InternalAlarm == va2.InternalAlarm); // False
        Console.WriteLine(va1.InternalAlarm.Equals(va2.InternalAlarm)); // True
    }

    public interface IAlarm
    {
        int Prop1 { get; }
        int Prop2 { get; }
    }

    public record MyAlarm : IAlarm
    {
        public int Prop1 { get; init; }
        public int Prop2 { get; init; }
    }

    public record VisibleAlarm
    {
        public IAlarm InternalAlarm { get; init; }
        public string Message       { get; set; }
    }
}

Output:

True
True
False
True

The reason this isn't working for you is because InternalAlarm is of type IAlarm and not MyAlarm. If you change the type to MyAlarm, you'll get the comparison as true rather than false.

This is because IAlarm does not define an operator== (note: such an operator would be static).

  • Related