Comparing two structs using ==
Asked Answered
D

8

82

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?

Ditheism answered 4/3, 2013 at 10:9 Comment(14)
How about using Equals()?Electrochemistry
Equals works fine, just wondering why my override of == is not workingDitheism
@JMK, maybe because you didn't override it... :)Fidgety
So I didn't, I haven't had any coffee yet hah thanks everyone!Ditheism
if (obj == null || GetType() != obj.GetType()) is a very strange way to write if(!(obj is CisSettings)).Johny
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.Johny
Also, calling GetHashCode on a 32 bit integer is unnecessary; a 32 bit integer is its own hash code.Johny
Also, your hash code has bad distribution if the four numbers tend to be similar values.Johny
@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!Ditheism
@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.Scruggs
@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 againDitheism
Does ReSharper really spit out code like that for a struct? It is useful for non-sealed classes, like I said.Scruggs
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)Ditheism
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()Echelon
O
123

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);
}
Ordway answered 4/3, 2013 at 10:13 Comment(3)
don't forget to overload "!=" operator also ;)Lautrec
"Probably" has nothing to do with it; it is illegal to override == without overriding !=.Johny
if you care about parformance you should consider implementing the == operator yourself. The above implementaiton causes boxing, which as described by Microsoft is "computationally expensive". Especially since you could just compare the 4 ints that your struct consists of.Prolate
P
11

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.

Pean answered 4/3, 2013 at 10:14 Comment(2)
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.Chopstick
@Chopstick .Equals() is often used for value-equality while == is left to remain as reference-equalityOverstudy
T
5

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
    }
}
Tobar answered 6/12, 2021 at 10:1 Comment(0)
A
3

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.

Appetite answered 4/3, 2013 at 10:16 Comment(0)
M
2

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

Majormajordomo answered 4/3, 2013 at 10:14 Comment(0)
W
2

You should overload your operator is some way like this:

public static bool operator ==(CisSettings a, CisSettings b)
{
    return a.Equals(b);
}
Wyon answered 4/3, 2013 at 10:15 Comment(0)
L
1

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

For overloading operator, see this page

Lautrec answered 4/3, 2013 at 10:20 Comment(0)
D
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;
}
Dichy answered 27/2, 2021 at 13:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.