Как заставить число находиться в диапазоне в С#?
В С# мне часто приходится ограничивать целочисленное значение диапазоном значений. Например, если приложение ожидает процент, целое число от пользовательского ввода не должно быть меньше нуля или более ста. Другой пример: если есть пять веб-страниц, к которым осуществляется доступ через Request.Params["p"]
, я ожидаю значение от 1 до 5, а не 0 или 256 или 99999.
Я часто заканчиваю написанием довольно уродливого кода вроде:
page = Math.Max(0, Math.Min(2, page));
или даже более уродливый:
percentage =
(inputPercentage < 0 || inputPercentage > 100) ?
0 :
inputPercentage;
Разве нет более разумного способа делать такие вещи в .NET Framework?
Я знаю, что могу написать общий метод int LimitToRange(int value, int inclusiveMinimum, int inlusiveMaximum)
и использовать его в каждом проекте, но, возможно, в рамках уже существует волшебный метод?
Если мне нужно сделать это вручную, какой был бы "лучший" (т.е. менее уродливый и быстрый) способ сделать то, что я делаю в первом примере? Что-то вроде этого?
public int LimitToRange(int value, int inclusiveMinimum, int inlusiveMaximum)
{
if (value >= inclusiveMinimum)
{
if (value <= inlusiveMaximum)
{
return value;
}
return inlusiveMaximum;
}
return inclusiveMinimum;
}
Ответы
Ответ 1
Я вижу ответ Марка и подниму его на this
:
public static class InputExtensions
{
public static int LimitToRange(
this int value, int inclusiveMinimum, int inclusiveMaximum)
{
if (value < inclusiveMinimum) { return inclusiveMinimum; }
if (value > inclusiveMaximum) { return inclusiveMaximum; }
return value;
}
}
Использование:
int userInput = ...;
int result = userInput.LimitToRange(1, 5)
Смотрите: Методы расширения
Ответ 2
Эта операция называется "Clamp", и она обычно записывается следующим образом:
public static int Clamp( int value, int min, int max )
{
return (value < min) ? min : (value > max) ? max : value;
}
Ответ 3
Более чистый метод, который будет работать не только с целыми числами (взятыми из моей собственной библиотеки общего кода):
public static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
{
if (value.CompareTo(min) < 0)
return min;
if (value.CompareTo(max) > 0)
return max;
return value;
}
Ответ 4
Альтернативный способ записи вашей функции LimitToRange
заключается в следующем.
public int LimitToRange(int value, int inclusiveMinimum, int inclusiveMaximum)
{
if (value < inclusiveMinimum) { return inclusiveMinimum; }
if (value > inclusiveMaximum) { return inclusiveMaximum; }
return value;
}
Я думаю, что это немного легче понять, пока он все еще эффективен.
Ответ 5
Нет, для этой встроенной структуры нет никакого метода. Я полагаю, что это было опущено, потому что у вас уже есть Min
и Max
, поэтому вы можете выполнить его с помощью них.
Если вы пишете свой собственный метод для него, это не имеет большого значения, как вы его пишете. Если вы используете операторы if
или условный оператор ?
, он все равно будет компилироваться почти таким же кодом.
Ответ 6
Мне нравится ответ Гуффа, но я удивлен, что никто еще не опубликовал решение, используя Min/Max.
public int LimitInclusive(int value, int min, int max)
{
return Math.Min(max, Math.Max(value, min));
}
Ответ 7
Чтобы закрепить значения, не давая пользователям никаких отзывов о том, что введенное ими значение неверно, в целом, может быть, не отличная идея (IMHO). Это может привести к появлению тонких ошибок, которые трудно отлаживать, особенно когда значения min/max определяются во время выполнения.
Подумайте об этом. У вас есть 100 долларов на вашем банковском счете, и вы хотите перечислить 150 долларов своему другу. Хотели бы вы, чтобы ваша банковская система бросила InsufficientFundsException
или поговорила с вашим другом о том, что вы перевели 150 долларов, но он получил всего 100 долларов (при условии, что банк зафиксировал сумму перевода до 100, поскольку у вас недостаточно средств).
При этом вы также должны посмотреть на контракты с кодом.
public void MyFunction (Type input)
{
Contract.Requires(input > SomeReferenceValue);
Contract.Requires (input < SomeOtherReferencValue);
}
Это приведет к тому, что пользовательский ввод будет находиться в пределах диапазона.
Ответ 8
Мне нравится имя Clamp. Я бы предложил следующий класс
public class MathHelper
{
public static int Clamp (int value,int min,int max)
{
// todo - implementation
}
public static float Clamp (float value,float min,float max)
{
// todo - implementation
}
)
или если вы хотите использовать generics, то
public class MathHelper
{
public static T Clamp<T> (T value, T min, T max) where T : IComparable
{
// todo - implementation
T output = value;
if (value.CompareTo(max) > 0)
{
return max;
}
if (value.CompareTo(min) < 0)
{
return min;
}
return output;
}
}