77

I am trying to compare two structs using equals (==) in C#. My struct is below:

public struct CisSettings : IEquatable<CisSettings>
{
    public int Gain { get; private set; }
    public int Offset { get; private set; }
    public int Bright { get; private set; }
    public int Contrast { get; private set; }

    public CisSettings(int gain, int offset, int bright, int contrast) : this()
    {
        Gain = gain;
        Offset = offset;
        Bright = bright;
        Contrast = contrast;
    }

    public bool Equals(CisSettings other)
    {
        return Equals(other, this);
    }

    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
        {
            return false;
        }

        var objectToCompareWith = (CisSettings) obj;

        return objectToCompareWith.Bright == Bright && objectToCompareWith.Contrast == Contrast &&
               objectToCompareWith.Gain == Gain && objectToCompareWith.Offset == Offset;

    }

    public override int GetHashCode()
    {
        var calculation = Gain + Offset + Bright + Contrast;
        return calculation.GetHashCode();
    }
}

I am trying to have struct as a property in my class, and want to check to see if the struct is equal to the value I am trying to assign it to, before I go ahead and do so, so I am not indicating the property has changed when it hasn't, like so:

public CisSettings TopCisSettings
{
    get { return _topCisSettings; }
    set
    {
        if (_topCisSettings == value)
        {
            return;
        }
        _topCisSettings = value;
        OnPropertyChanged("TopCisSettings");
    }
}

However, on the line where I check for equality, I get this error:

Operator '==' cannot be applied to operands of type 'CisSettings' and 'CisSettings'

I can't figure out why this is happening, could somebody point me in the right direction?

JMK
  • 27,273
  • 52
  • 163
  • 280
  • How about using `Equals()`? – Andrey Gordeev Mar 04 '13 at 10:12
  • Equals works fine, just wondering why my override of == is not working – JMK Mar 04 '13 at 10:12
  • 4
    @JMK, maybe because you didn't override it... :) – gdoron Mar 04 '13 at 10:13
  • So I didn't, I haven't had any coffee yet hah thanks everyone! – JMK Mar 04 '13 at 10:16
  • 5
    `if (obj == null || GetType() != obj.GetType())` is a very strange way to write `if(!(obj is CisSettings))`. – Eric Lippert Mar 04 '13 at 16:09
  • 4
    Also, the logic is in the wrong place: put the type-specific logic in `Equals(CisSettings)` and have `Equals(object)` call it, rather than the other way around. – Eric Lippert Mar 04 '13 at 16:12
  • 4
    Also, calling `GetHashCode` on a 32 bit integer is unnecessary; a 32 bit integer is its own hash code. – Eric Lippert Mar 04 '13 at 16:13
  • 5
    Also, your hash code has bad distribution if the four numbers tend to be similar values. – Eric Lippert Mar 04 '13 at 16:14
  • @EricLippert In regards to point one, ReSharper wrote that, I should have changed it. Also I will move the type specific logic to the generic equals method, and I understand now that a 32-bit integer is its own hash code, thanks for the tips! – JMK Mar 04 '13 at 16:49
  • @EricLippert Quote: _. `if (obj == null || GetType() != obj.GetType())` is a very strange way to write `if(!(obj is CisSettings))`_ Sure, it looks like he's imitating the preferred way to do `Equals` with non-sealed classes. There it is good practice to check if either `this` or `other` is more derived. But of course with a sealed type, like a struct in this case, there's no need for that. – Jeppe Stig Nielsen Mar 04 '13 at 22:18
  • @JeppeStigNielsen I wasn't being that smart, I was just being lazy and letting ReSharper write the code for me, but yourself and Eric are both correct, thanks again – JMK Mar 04 '13 at 22:26
  • Does ReSharper really spit out code like that for a struct? It is useful for non-sealed classes, like I said. – Jeppe Stig Nielsen Mar 04 '13 at 22:31
  • ReSharper or Visual Studio 2012, not sure which now! But type the word equals and press return and that's what comes out (along with come comments linking to guidelines on Microsoft.com, which is making me think it's not ReSharper at all but VS) – JMK Mar 04 '13 at 23:05
  • 2
    Many of the answers mentioned that the solution is to overload `==` and `!=`. But I think it's more important to answer this question: **why doesn't `struct` implementation `==` and `!=` by default, using the already implemented `Equals()`** – KFL Nov 15 '17 at 20:52

8 Answers8

120

You need to overload the == and != operators. Add this to your struct:

public static bool operator ==(CisSettings c1, CisSettings c2) 
{
    return c1.Equals(c2);
}

public static bool operator !=(CisSettings c1, CisSettings c2) 
{
   return !c1.Equals(c2);
}
Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
Jens Kloster
  • 11,099
  • 5
  • 40
  • 54
  • 4
    don't forget to overload "!=" operator also ;) – Xaruth Mar 04 '13 at 10:21
  • 22
    "Probably" has nothing to do with it; it is *illegal* to override == without overriding !=. – Eric Lippert Mar 04 '13 at 16:07
  • 8
    if you care about parformance you should consider implementing the `==` operator yourself. The above implementaiton causes `boxing`, which as described by [Microsoft](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing) is "computationally expensive". Especially since you could just compare the 4 ints that your struct consists of. – inwenis Sep 28 '18 at 20:39
10

When you override the .Equals() method, the == operator is not automatically overloaded. You need to do that explicitly.

See also Guidelines for Overriding Equals() and Operator == or CA1815: Override equals and operator equals on value types.

Hans Kesting
  • 38,117
  • 9
  • 79
  • 111
  • 1
    Just out of curiosity, is there a circumstance in which you'd want `Equals()` and `==` to return different values? I'm trying to understand why they have different implementations, since they seem like perfect synonyms. – Nerrolken Mar 11 '21 at 22:47
  • 1
    @Nerrolken .Equals() is often used for value-equality while == is left to remain as reference-equality – Vapid May 06 '21 at 07:13
3

You can also use Record types since C# v9 and [record struct] value types since C# v10 to avoid writing tremendous amount of writing repeated code without any point

For more details see Microsoft docs here:

Equality operators (C# reference)

Available in C# 9.0 and later, record types support the == and != operators that by default provide value equality semantics. That is, two record operands are equal when both of them are null or corresponding values of all fields and auto-implemented properties are equal.

public class RecordTypesEquality
{
    public record Point(int X, int Y, string Name);
    public record TaggedNumber(int Number, List<string> Tags);

    public static void Main()
    {
        var p1 = new Point(2, 3, "A");
        var p2 = new Point(1, 3, "B");
        var p3 = new Point(2, 3, "A");

        Console.WriteLine(p1 == p2);  // output: False
        Console.WriteLine(p1 == p3);  // output: True

        var n1 = new TaggedNumber(2, new List<string>() { "A" });
        var n2 = new TaggedNumber(2, new List<string>() { "A" });
        Console.WriteLine(n1 == n2);  // output: False
    }
}
Bijan
  • 106
  • 4
2

You don't implement explicitly an equality operator, so == is not defined particularly for the type.

Grant Thomas
  • 44,454
  • 10
  • 85
  • 129
2

You should overload your operator is some way like this:

public static bool operator ==(CisSettings a, CisSettings b)
{
    return a.Equals(b);
}
Denys Denysenko
  • 7,598
  • 1
  • 20
  • 30
2

You need to override operator == explicitly.

public static bool operator ==(CisSettings x, CisSettings y) 
{
   return x.Equals(y);
}

By the way, you'd better put the comparing code in public bool Equals(CisSettings other), and let bool Equals(object obj) call bool Equals(CisSettings other), so that you can gain some performance by avoiding unnecessary type check.

Chen
  • 1,654
  • 2
  • 13
  • 21
1

you must overload "==" operator, but also overload "!=" operator. (Look at this Note)

For overloading operator, see this page

Xaruth
  • 4,034
  • 3
  • 19
  • 26
0

Make a method and pass both stuct obj as parameter do compariosn one by one

public Save ReturnGreater(Save online,Save local)
{
    Save DataToSave = new Save();
    DataToSave.Score = local.Score < online.Score ? online.Score : local.Score;
    DataToSave.UnlockGuns = local.UnlockGuns < online.UnlockGuns ? online.UnlockGuns : local.UnlockGuns;
    DataToSave.UnlockLevels = local.UnlockLevels < online.UnlockLevels ? online.UnlockLevels : local.UnlockLevels;
    DataToSave.TotalLevels = local.TotalLevels;
    DataToSave.RemoveAds = local.RemoveAds;
    return DataToSave;
}
juagicre
  • 1,065
  • 30
  • 42