Подтвердить что ты не робот

Как заставить число находиться в диапазоне в С#?

В С# мне часто приходится ограничивать целочисленное значение диапазоном значений. Например, если приложение ожидает процент, целое число от пользовательского ввода не должно быть меньше нуля или более ста. Другой пример: если есть пять веб-страниц, к которым осуществляется доступ через 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;
}
4b9b3361

Ответ 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;
     } 
}