Рассмотрим эту тривиальную функцию:
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
).
Приведенный выше пример кода явно совершенно бессмыслен. Но мой более широкий вопрос: не будет ли всегда иметь смысл определять методы последним способом (с использованием общего ограничения, а не с параметром некоторого типа интерфейса), чтобы избежать потенциальной упаковки типов значений?
Я подозреваю, что ответ, скорее всего, будет «да», но это требует большего набора текста, и во многих случаях встреча с типом значения будет очень маловероятной, например, когда метод принимает некоторые IEnumerable
«. Но мне интересно, есть ли большая разница между этими подходами, которые избегают меня в данный момент.
Одна из проблем связана с тем, что общее ограничение на самом деле не является частью подписи. Если у вас есть ...
static T Method<T>(T value) where T : ICompareable<int>
... и ...
static T Method<T>(T value) where T : IEnumerable<int>
... у компилятора не будет способа узнать, кто из них кто.
И, ссылаясь на Эрика Липперта...
В комментарии к вопросу о том, вызывает ли вызов метода бокс после того, как передан аргумент.
Когда вы вызываете виртуальный метод для выражения, тип которого является параметром типа с ограничением на него, компилятор C # выдает инструкцию constrained.callvirt
. Как можно было бы надеяться, это правильно; бокс случается только тогда, когда это абсолютно необходимо.
Подробнее о точной семантике упаковки ограниченных виртуальных вызовов см. В документации:
http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.constrained.aspx
Другая проблема может возникнуть, если универсальное ограничение относится к параметрам универсального типа параметризованного типа, например
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>
Это просто не выглядит правильным.