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?
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>
.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With