Carles Company
Carles Company

Reputation: 7216

Generic constraints

Which version is better:

using System;

namespace Utils.Extensions
{
    public static class EnumerableExtensions
    {
        public static bool Between<T>(this T item, T min, T max) where T : IComparable<T>
        {
            return item.CompareTo(min) >= 0 && item.CompareTo(max) <= 0;
        }
    }
}

or

using System;

namespace Utils.Extensions
{
    public static class EnumerableExtensions
    {
        public static bool Between<T>(this IComparable<T> item, T min, T max)
        {
            return item.CompareTo(min) >= 0 && item.CompareTo(max) <= 0;
        }
    }
}

I think both should work, but which one should I use?

Upvotes: 1

Views: 243

Answers (4)

Mark H
Mark H

Reputation: 13907

There's an obvious difference between the two. The first one is expected to be used on a type that implements IComparable<T>, where the second one is meant to be use on an IComparable<T>.

Given example A:

IComparable<int> x = 14;
bool y = x.Between(12, 23); 

Contrast to example B:

int x = 14;
bool y = x.Between(12, 23);

If you attempt to use example A with your first technique (the generic constraint), then you are going to get a compiler error, because the constraint requires a type that implements IComparable<int>, and not an IComparable<int> itself.

Example B will compile using both techniques. I would advise using the second technique so that it works the same in both situations. It appears to be the convention anyway, probably for this reason. But there are other reasons to avoid using the first one anyway. In situations where you might wish to overload a method for particular types, the second method gives you more flexibility.

Upvotes: 2

Marnix
Marnix

Reputation: 6547

I would say the first one, because it says something about every T, where the second one only says that item should be IComparable. Indeed it should do the same, but by means of what you want to say, that every T is IComparable. The first one is better.

Upvotes: -1

LukeH
LukeH

Reputation: 269628

Note that using the second version when T is a value-type means that item will be boxed. Casting a value-type to an interface always requires boxing.

I wouldn't worry too much about this for interfaces in general, but it's worth noting that almost every IComparable<T> implementation in the BCL is a value-type. Only you can decide whether the (small) boxing overhead is acceptable.

Upvotes: 3

decyclone
decyclone

Reputation: 30840

I think the second one is better. It won't clutter up intelliSense suggestion list for types that are not IComparable. (It won't in any case.)

And what you are looking for is specific to IComparable.

So, it is clear enough to use the second approach if what you are opting for is readability.

Upvotes: 3

Related Questions