0

I've implemented the iEquatable interface:

LineItem : IEquatable<LineItem>

But now I want to debug my Equals(...) method by stepping through the code. But even in debug mode, stepping in doesn't go into it (i.e. F11), and putting a breakpoint inside the method doesn't get me into it either. How can I debug it??

Not that it should be relevant but here is my Equals method:

public bool Equals(LineItem other)
            {
                List<bool> individuals = new List<bool>();

                individuals.Add(DateTime.Equals(Expiry, other.Expiry));
                individuals.Add(Code == other.Code);
                individuals.Add(Type == other.Type);
                individuals.Add(Class == other.Class);

                Func<object, object, bool> Compare = delegate(object A, object B)
                {
                    if (A == DBNull.Value || B == DBNull.Value)
                        return A == B;
                    else
                        return (double)A == (double)B;
                };

                individuals.Add(Compare(Strike, other.Strike));
                individuals.Add(Compare(Future, other.Future));
                individuals.Add(Compare(Premium, other.Premium));
                individuals.Add(Compare(Volatility, other.Volatility));
                individuals.Add(Compare(Volume, other.Volume));
                individuals.Add(Compare(OpenInterest, other.OpenInterest));
                individuals.Add(Compare(Delta, other.Delta));

                return !individuals.Contains(false);
            }

EDIT: I'm calling the method from elsewhere in my code like this now:

if(!fo.Future.Equals(li))...

but that still doesn't let me debug it.

Dan
  • 45,079
  • 17
  • 88
  • 157
  • Have u checked if the debug symbols are properly pointed? – Rameez Ahmed Sayad Jul 26 '13 at 16:20
  • Usually a VS dialogue box shows up saying that it will step over your code. Are you getting/have got this? (you could have hit "no" which will hide the box from all future occurrences). – gunr2171 Jul 26 '13 at 16:21
  • 1
    Are you sure your method is being called? – Eric Lippert Jul 26 '13 at 16:21
  • My psychic debugging skills tell me that you're stepping into `object.Equals(object)` – SLaks Jul 26 '13 at 16:21
  • @EricLippert Pretty certain, here is the line that calls it `if (!LineItem.Equals(fo.Future, li))` – Dan Jul 26 '13 at 16:22
  • Please provide a *full, self-contained* example that demonstrates the issue (i.e. including the call to `Equals`, stripped down to the bare necessities to demonstrate the problem). – Heinzi Jul 26 '13 at 16:23
  • @RameezAhmedSayad I don't know what that means. – Dan Jul 26 '13 at 16:23
  • @gunr2171 No I don't get any dialogue – Dan Jul 26 '13 at 16:24
  • 2
    If that is your call, then you are calling `static object.Equals(object, object)`, which is not your method. Try `fo.Future.Equals(li)`. – Heinzi Jul 26 '13 at 16:24
  • If the code you're calling is not in the same assembly and you're referencing the dll ... When in debug mode Goto Debug->Windows->Modules and check for the path of the dlls. – Rameez Ahmed Sayad Jul 26 '13 at 16:27
  • @Dan: I am pretty certain that your certainty is misplaced. You can't call an instance method as though it was a static method. – Eric Lippert Jul 26 '13 at 16:30
  • The syntax you want is probably `fo.Future.Equals(li)`. I will also take this opportunity to note that it is a good practice to make `Equals(object)` and `==` all do the same thing if you are going to make `Equals(SomeClass)` implement value equality. It is extremely confusing to have `x.Equals(y)` and `x == y` mean completely different things. – Eric Lippert Jul 26 '13 at 16:33
  • 2
    And while we are criticizing your code: it seems very inefficient. Why do you do *all* those comparisons and *then* check to see if any is false? If the first one is false then you don't need to do any of the rest. – Eric Lippert Jul 26 '13 at 16:36
  • 1
    And also: start your `Equals` method with `if (ReferenceEquals(this, other)) return true;` That way you skip doing all that work if you know for certain that it will be true. – Eric Lippert Jul 26 '13 at 16:37
  • I agree with everyone that `fo.Future.Equals(li)` is obviously correct.But it still isn't letting me debug :/ and also I get build erros if I add `override` before my `Equals` method – Dan Jul 26 '13 at 16:38
  • Don't override *your* equals method. Override *the built-in Equals method* to *use* your method. – Eric Lippert Jul 26 '13 at 16:50
  • You need to ask a more general question here, namely, "how do I correctly implement a custom equality method?" There is a correct pattern to follow for doing this and odds are good that you're not doing it right. – Eric Lippert Jul 26 '13 at 16:51
  • Based on your experience so far today, what do you think is more likely: the debugger is broken, or you are still not calling your method correctly? – Eric Lippert Jul 26 '13 at 16:52
  • @EricLippert obviously I know the debugger isn't broken! I assumed implementing the interface meant that it would use my Equals method... I think my issue now if that sometimes the instance is null so it errors trying to call Equals rather then inside Equals... I still don't understand about the overriding though – Dan Jul 26 '13 at 16:55
  • *the debugger is broken* .. kinda humorous – Ken Kin Jul 26 '13 at 17:11

2 Answers2

15

You need to take a big step back and learn how to implement equality methods correctly in the first place. C# was designed to be a "pit of success" language; that is, you should naturally "fall into" doing things the right way. Unfortunately, equality is not a "pit of success" in C#; the language designers failed to make it easy to do it right the first time.

Here's the pattern that I use when I override equality.

First, start by writing a private static method that does everything right. Everything else will use this method. Start your method by dealing with (1) the reference equality early out, and (2) null checks.

private static MyEquality(Foo x, Foo y)
{
  if (ReferenceEquals(x, y)) return true;
  // We now know that they are not BOTH null.  If one is null
  // and the other isn't then they are not equal.
  if (ReferenceEquals(x, null)) return false;
  if (ReferenceEquals(y, null)) return false;
  // Now we know that they are both non-null and not reference equal.
  ... check for value equality here ...
}

OK, now that we have that, we can use that to implement everything else.

public override bool Equals(object y)
{
  return MyEquality(this, y as Foo);
}
public override int GetHashcode()
{
  // Implement GetHashcode to follow the Prime Directive Of GetHashcode:
  // Thou shalt implement GetHashcode such that if x.Equals(y) is true then 
  // x.GetHashcode() == y.GetHashcode() is always also true.
}
public bool Equals(Foo y)
{
  return MyEquality(this, y);
}

That is what is necessary to correctly implement IEquatable<T>.Equals. You should also consider overriding the == operator to be consistent:

public static bool operator ==(Foo x, Foo y)
{
    return MyEquality(x, y);
}
public static bool operator !=(Foo x, Foo y)
{
    return !MyEquality(x, y);
}

Now no matter whether you call object.Equals(foo, bar), foo.Equals(bar), or foo == bar, you have consistent behavior.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Thanks for the explanation, although I'm still not sure where iEquatable fits in then? Anyway, the issue was that I was calling the object method instead of the instance method. Now I can debug! – Dan Jul 26 '13 at 17:10
  • 1
    @Dan: `IEquatable` requires that you implement `public bool Equals(Foo foo)`. I did so. – Eric Lippert Jul 26 '13 at 17:11
  • @Dan: A more precise characterization is: the issue was that *the object method* and *your method* did *different things*. They should always do *the same thing*, which they do if you follow the correct pattern, as I described. – Eric Lippert Jul 26 '13 at 17:12
  • Oh right, so we're assuming the class all that live in has a `:iEquatable` afterwards. Thanks! – Dan Jul 26 '13 at 17:13
  • Yeah but in this case I'm happy for the object method and the `==` method to compare instances rather than values. But I'll keep this in mind for the future – Dan Jul 26 '13 at 17:18
  • @Dan: Doing so is an extremely poor programming practice. The .NET Framework Design Guidelines indicate that it is *reasonable* to make `==` mean reference equality and `Equals` mean value equality but many people myself included find classes which do so to be confusing. It is definitely a poor practice to make `Equals(Foo)` have value semantics and `Equals(object)` (or, equivalently, `Equals(object, object)`) to have reference semantics. That is a recipe for bugs. – Eric Lippert Jul 26 '13 at 18:16
  • Eric, thank you for clarying. People thought I am nuts in overriding the equality operator for reference types. Look at this FxCop rule, http://msdn.microsoft.com/en-us/library/ms182145(v=vs.80).aspx , which says not to overload Equals operator for reference type. I think this is what Java did with their string types. To compare strings, you had to use Equals method as the equals operator would only do reference comparison. – SolutionYogi Jul 26 '13 at 18:44
  • 5
    @SolutionYogi: I don't think you're nuts. :-) For me, the question is whether reference types ought to have value equality at all. My preferences are from best to worst: (1) reference types use reference equality, (2) reference types use value equality consistently for all forms of equality, (3) reference types use value equality for `Equals` and reference equality for `==`, and (4) everything else. – Eric Lippert Jul 26 '13 at 18:50
  • @Dan `IEquatable.Equals` should be identical to `object.Equals`. Its main point is that calling it avoids boxing for value-types. | One point Eric didn't mention concerning good vs. bad equals is that custom equality is usually a bad idea on mutable types. A good example of custom equality is `System.String` which is immutable and all of the above methods (`object.Equals`, `IEquatable.Equals`, `==`, `!=` and `GetHashCode`) are consistent. – CodesInChaos Aug 16 '13 at 16:33
4

LineItem.Equals(a, b) is a static method call to Object.Equals(object, object); it isn't your method.

This implementation will call a.Equals(object) if you've overridden it, but you did not override it.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • I don't understand your second point about the overriding? I agree with the first, but changing it as in my edit hasn't helped :( – Dan Jul 26 '13 at 16:46
  • @Dan: `li` is probably of compile-time type `Object`, so you're calling `object.Equals(object)` and not your function. You should probably override `Equals(object)` to call your function. – SLaks Jul 26 '13 at 16:53
  • But which `Equals(object)`? inside my `LineItem` class? I get an error if I try that – Dan Jul 26 '13 at 16:57