Следует ли использовать общие ограничения для использования интерфейсов в качестве типов параметров?

Рассмотрим эту тривиальную функцию:

public static bool IsPositive(IComparable<int> value)
{
    return value.CompareTo(0) > 0;
}

Теперь, если я передаю int этому методу, он получает коробку. Не было бы поэтому лучше определить вышеупомянутый метод следующим образом:

public static bool IsPositive<T>(T value) where T : IComparable<int>
{
    return value.CompareTo(0) > 0;
}

Используя общее ограничение таким образом, я могу достичь точно такой же функциональности, как и код выше, с дополнительным преимуществом, которое не требует бокса (так как вызов IsPositive<int> принимает параметр типа int).

Приведенный выше примерный код явно не имеет смысла. Но мой более широкий вопрос: , не всегда ли смысл определять методы последним способом (используя общее ограничение, а не иметь параметр какого-либо типа интерфейса), чтобы избежать потенциального бокса типов значений?

Я подозреваю, что ответ скорее всего будет "да", но он требует большего набора текста, и во многих случаях встретить тип значения будет очень маловероятным, например, когда метод принимает некоторые IEnumerable<T>. " Но мне интересно, есть ли большая разница между этими подходами, которые ускользают от меня в данный момент.

Ответы

Ответ 1

Одна проблема заключается в том, что общий ограничитель не является частью подписи. Если у вас есть...

static T Method<T>(T value) where T : ICompareable<int>

... и...

static T Method<T>(T value) where T : IEnumerable<int>

... у компилятора не было бы способа узнать, что есть.

И чтобы вызвать Эрика Липперта...

Ответ 2

В комментариях к вопросу возникла некоторая путаница в вопросе о том, вызывает ли вызов метода метод бокса после передачи аргумента.

Когда вы вызываете виртуальный метод выражения, тип которого является параметром типа с ограничением на него, компилятор С# испускает команду constrained.callvirt. Как можно надеяться, это делает правильные вещи; бокс происходит только тогда, когда это абсолютно необходимо.

Подробнее о точной семантике бокса связанных виртуальных вызовов читайте в документации:

http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.constrained.aspx

Ответ 3

Другая проблема будет заключаться в том, что общее ограничение относится к параметрическим типам параметризованного типа, таким как

static bool AreAllTheSame<T>(IEnumerable<T> something)
  where T : IEquatable<T>

Не всегда возможно преобразовать параметр типового типа таким образом, если вы не вводите параметр второго типа, например:

static bool AreAllTheSame<S, T>(S something)
  where S : IEnumerable<T>
  where T : IEquatable<T>

Это просто не выглядит правильным.