6

I want to do something like this:

public override int CompareTo (Foo rhs)
{
    return Bar.CompareTo(rhs.Bar) ??
           Baz.CompareTo(rhs.Baz) ??
           Fuz.CompareTo(rhs.Fuz) ?? 0;
}

This doesn't work as written; is there some minimal workaround to make it work? Basically I want 0 to chain until non-zero (or end of chain).

Matt Chambers
  • 2,229
  • 1
  • 25
  • 43
  • I don't know the answer to your question, but if three values can be used to determine the return value, you might want to consider redesigning your class. – DJ Quimby Oct 24 '11 at 20:09
  • @DJ `CompareTo` is related to sorting; that is pretty common, actually - for example, sort by Surname - if that is the same, then sort by FirstName, then if still equal, sort by some unique identifier (database id, for example). – Marc Gravell Oct 24 '11 at 20:10
  • @Marc Interesting, I haven't been exposed to this before. I see that CompareTo is part of the IComparable class. I'll have to read more to get some perspective. Thanks – DJ Quimby Oct 24 '11 at 20:18
  • Are the methods `CompareTo` returning null? `??` is only working with null. – Fischermaen Oct 24 '11 at 20:10

5 Answers5

3

Not supported by the language. But you can write a small helper like this:

public override int CompareTo (Foo rhs)
{
    return FirstNonZeroValue(
        () => Bar.CompareTo(rhs.Bar), 
        () => Baz.CompareTo(rhs.Baz),
        () => Fuz.CompareTo(rhs.Fuz));
}

private int FirstNonZeroValue(params Func<int>[] comparisons)
{
    return comparisons.Select(x => x()).FirstOrDefault(x => x != 0);
}
driis
  • 161,458
  • 45
  • 265
  • 341
2

No basically, but it would be nice if it did (IIRC, Jon mentioned a similar idea in C# in Depth). You could probably chain conditionals, but I tend to just use:

int delta = Bar.CompareTo(rhs.Bar);
if(delta == 0) delta = Baz.CompareTo(rhs.Baz);
if(delta == 0) delta = Fuz.CompareTo(rhs.Fuz);
return delta;
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
2

Not really, ?? only works for null values (reference types or nullable structs)

int i;

i = Bar.CompareTo(rhs.Bar);
if (i != 0) return i;

i = Baz.CompareTo(rhs.Baz);
if (i != 0) return i;

i = Fuz.CompareTo(rhs.Fuz);
if (i != 0) return i;

return 0;
thecoop
  • 45,220
  • 19
  • 132
  • 189
0

If short-circuiting is not needed, you can do some binary arithmetic. It is not required that the Compare results must be -1, 0, or 1, but that they must be < 0, = 0, > 0. So by using 2^n as a coefficient where n is the precedence, you can sum up the CompareTo results and get a number >0 or <0 as needed.

So, use

return 4 * Bar.CompareTo(rhs.Bar)
     + 2 * Baz.CompareTo(rhs.Baz)
     + 1 * Fuz.CompareTo(rhs.Fuz);
Pxtl
  • 880
  • 8
  • 18
0

You can also change zero to null with a function and get lazy evaluation without Func<> :

public override int CompareTo (Foo rhs) =>
    Bar.CompareTo(rhs.Bar).GetNullIfZero() ??
    Baz.CompareTo(rhs.Baz).GetNullIfZero() ??
    Fuz.CompareTo(rhs.Fuz);

public static class IntExtensions
{
    public static int? GetNullIfZero(this int i) => i == 0 ? null : (int?)i;
}

There's a similar way for lazy chaining string operations with a GetNullIfEmpty and GetNullIfWhiteSpace extensions.

McX
  • 1,296
  • 2
  • 12
  • 16