Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interface constraint for IComparable

When I want to constraint the type T to be comparable, should I use:

where T : IComparable

or

where T : IComparable<T>

I can't get my head around if #2 makes sense. Anyone can explain what the difference would be?

like image 435
Joan Venge Avatar asked May 27 '09 16:05

Joan Venge


3 Answers

You may want both constraints, as in:

where T : IComparable, IComparable<T>

This would make your type compatible with more users of the IComparable interfaces. The generic version of IComparable, IComparable<T> will help to avoid boxing when T is a value type and allows for more strongly typed implementations of the interface methods. Supporting both ensures that no matter which interface some other object asks for, your object can comply and therefore inter-operate nicely.

For example, Array.Sort and ArrayList.Sort use IComparable, not IComparable<T>.

like image 199
Jeff Yates Avatar answered Nov 15 '22 14:11

Jeff Yates


The main difference between IComparable and IComparable<> is that the first is pre-generics so allows you to call the compare method with any object, whereas the second enforces that it shares the same type:

IComparable - CompareTo(object other);
IComparable<T> - CompareTo(T other);

I would go with the second option provided that you don't intend to use any old .net 1.0 libraries where the types may not implement the modern, generic solution. You'll gain a performance boost since you'll avoid boxing and the comparisons won't need to check the types match and you'll also get the warm feeling that comes from doing things in the most cutting edge way...


To address Jeff's very good and pertinent point I would argue that it is good practice to place as few constraints on a generic as is required to perform the task. Since you are in complete control of the code inside the generic you know whether you are using any methods that require a basic IComparable type. So, taking his comment into consideration I personally would follow these rules:

  • If you are not expecting the generic to use any types that only implement IComparable (i.e. legacy 1.0 code) and you are not calling any methods from inside the generic that rely on an IComparable parameter then use the IComparable<> constraint only.

  • If you are using types that only implement IComparable then use that constraint only

  • If you are using methods that require an IComparable parameter, but not using types that only implement IComparable then using both constraints as in Jeff's answer will boost performance when you use methods that accept the generic type.

To expand on the third rule - let's assume that the class you are writing is as follows:

public class StrangeExample<T> where ... //to be decided
{
    public void SortArray(T[] input)
    {
         Array.Sort(input);
    }

    public bool AreEqual(T a, T b)
    {
        return a.CompareTo(b) == 0;
    }
}

And we need to decide what constraints to place on it. The SortArray method calls Array.Sort which requires the array that is passed in to contains objects that implement IComparable. Therefore we must have an IComparable constraint:

public class StrangeExample<T> where T : IComparable

Now the class will compile and work correctly as an array of T is valid for Array.Sort and there is a valid .CompareTo method defined in the interface. However, if you are sure that you will not want to use your class with a type that does not also implement the IComparable<> interface you can extend your constraint to:

public class StrangeExample<T> where T : IComparable, IComparable<T>

This means that when AreEqual is called it will use the faster, generic CompareTo method and you will see a performance benefit at the expense of not being able to use it with old, .NET 1.0 types.

On the other hand if you didn't have the AreEqual method then there is no advantage to the IComparable<> constraint so you may as well drop it - you are only using IComparable implementations anyway.

like image 27
Martin Harris Avatar answered Nov 15 '22 15:11

Martin Harris


Those are two different interfaces. Before .NET 2.0 there were no generics, so there was just IComparable. With .NET 2.0 came generics and it became possible to make IComparable<T>. They do exactly the same. Basically IComparable is obsolete, although most libraries out there recognize both.

To make your code really compatible, implement both, but make one call the other, so you don't have to write the same code twice.

like image 34
Vilx- Avatar answered Nov 15 '22 14:11

Vilx-