Как элегантно проверить, находится ли число в пределах диапазона? - PullRequest
129 голосов
/ 06 июля 2010

Как я могу сделать это элегантно с C # и .NET 3.5 / 4?

Например, число может быть между 1 и 100.

Я знаю простого if, если его будет достаточно;но ключевое слово в этом вопросе - элегантность.Это для моего игрушечного проекта не для производства.

Этот вопрос был не о скорости, а о красоте кода.Хватит говорить об эффективности и тому подобном;помните, что вы проповедуете хору.

Ответы [ 23 ]

2 голосов
/ 20 сентября 2013

Если вы хотите написать больше кода, чем просто if, может быть, вы можете: Создайте метод расширения с именем IsBetween

public static class NumberExtensionMethods
{
    public static bool IsBetween(this long value, long Min, long Max)
    {
        // return (value >= Min && value <= Max);
        if (value >= Min && value <= Max) return true;
        else return false;
    }
}

...

// Checks if this number is between 1 and 100.
long MyNumber = 99;
MessageBox.Show(MyNumber.IsBetween(1, 100).ToString());

Приложение: Стоит отметить, что на практике вы очень редко "просто проверяете на равенство" (или <,>) в базе кода. (За исключением самых тривиальных ситуаций.) Чисто в качестве примера, любой программист игры будет использовать категории, подобные приведенным ниже, в каждом проекте в качестве основного вопроса. Обратите внимание, что в этом примере (случается, что) используется функция (Mathf.Approximately), которая встроена в эту среду; на практике вам, как правило, приходится тщательно разрабатывать свои собственные представления о том, что означает сравнение для компьютерного представления действительных чисел, для типа создаваемой вами ситуации. (Даже не упоминайте, что если вы делаете что-то вроде, например, контроллера, ПИД-регулятора или чего-то подобного, весь вопрос становится центральным и очень сложным, он становится природой проекта.) В любом случае ОП вопрос здесь тривиальный или неважный вопрос.

private bool FloatLessThan(float a, float b)
    {
    if ( Mathf.Approximately(a,b) ) return false;
    if (a<b) return true;
    return false;
    }

private bool FloatLessThanZero(float a)
    {
    if ( Mathf.Approximately(a,0f) ) return false;
    if (a<0f) return true;
    return false;
    }

private bool FloatLessThanOrEqualToZero(float a)
    {
    if ( Mathf.Approximately(a,0f) ) return true;
    if (a<0f) return true;
    return false;
    }
2 голосов
/ 07 февраля 2017

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

public enum Range
{
    /// <summary>
    /// A range that contains all values greater than start and less than end.
    /// </summary>
    Open,
    /// <summary>
    /// A range that contains all values greater than or equal to start and less than or equal to end.
    /// </summary>
    Closed,
    /// <summary>
    /// A range that contains all values greater than or equal to start and less than end.
    /// </summary>
    OpenClosed,
    /// <summary>
    /// A range that contains all values greater than start and less than or equal to end.
    /// </summary>
    ClosedOpen
}

public static class RangeExtensions
{
    /// <summary>
    /// Checks if a value is within a range that contains all values greater than start and less than or equal to end.
    /// </summary>
    /// <param name="value">The value that should be checked.</param>
    /// <param name="start">The first value of the range to be checked.</param>
    /// <param name="end">The last value of the range to be checked.</param>
    /// <returns><c>True</c> if the value is greater than start and less than or equal to end, otherwise <c>false</c>.</returns>
    public static bool IsWithin<T>(this T value, T start, T end) where T : IComparable<T>
    {
        return IsWithin(value, start, end, Range.ClosedOpen);
    }

    /// <summary>
    /// Checks if a value is within the given range.
    /// </summary>
    /// <param name="value">The value that should be checked.</param>
    /// <param name="start">The first value of the range to be checked.</param>
    /// <param name="end">The last value of the range to be checked.</param>
    /// <param name="range">The kind of range that should be checked. Depending on the given kind of range the start end end value are either inclusive or exclusive.</param>
    /// <returns><c>True</c> if the value is within the given range, otherwise <c>false</c>.</returns>
    public static bool IsWithin<T>(this T value, T start, T end, Range range) where T : IComparable<T>
    {
        if (value == null)
            throw new ArgumentNullException(nameof(value));

        if (start == null)
            throw new ArgumentNullException(nameof(start));

        if (end == null)
            throw new ArgumentNullException(nameof(end));

        switch (range)
        {
            case Range.Open:
                return value.CompareTo(start) > 0
                       && value.CompareTo(end) < 0;
            case Range.Closed:
                return value.CompareTo(start) >= 0
                       && value.CompareTo(end) <= 0;
            case Range.OpenClosed:
                return value.CompareTo(start) > 0
                       && value.CompareTo(end) <= 0;
            case Range.ClosedOpen:
                return value.CompareTo(start) >= 0
                       && value.CompareTo(end) < 0;
            default:
                throw new ArgumentException($"Unknown parameter value {range}.", nameof(range));
        }
    }
}

Затем вы можете использовать его так:

var value = 5;
var start = 1;
var end = 10;

var result = value.IsWithin(start, end, Range.Closed);
1 голос
/ 30 марта 2019
static class ExtensionMethods
{
    internal static bool IsBetween(this double number,double bound1, double bound2)
    {
        return Math.Min(bound1, bound2) <= number && number <= Math.Max(bound2, bound1);
    }

    internal static bool IsBetween(this int number, double bound1, double bound2)
    {
        return Math.Min(bound1, bound2) <= number && number <= Math.Max(bound2, bound1);
    }
}

Использование

double numberToBeChecked = 7;

var result = numberToBeChecked.IsBetween (100,122);

var result = 5.IsBetween (100,120);

var result = 8.0.IsBetween (1.2,9.6);

1 голос
/ 06 июля 2010

В C, если эффективность времени имеет решающее значение и целочисленные переполнения будут перенесены, можно сделать if ((unsigned)(value-min) <= (max-min)) .... Если 'max' и 'min' являются независимыми переменными, дополнительное вычитание для (max-min) будет тратить время, но если это выражение можно предварительно вычислить во время компиляции или если его можно вычислить один раз во время выполнения, чтобы проверить множество числа против одного и того же диапазона, приведенное выше выражение может быть эффективно вычислено даже в случае, когда значение находится в пределах диапазона (если большая часть значений будет ниже допустимого диапазона, может быть быстрее использовать if ((value >= min) && (value <= max)) ..., потому что оно будет выход рано , если значение меньше мин.)

Прежде чем использовать такую ​​реализацию, сравните с целевой машиной. На некоторых процессорах выражение из двух частей может быть быстрее во всех случаях, так как два сравнения могут выполняться независимо, тогда как в методе вычитания и сравнения вычитание должно быть выполнено до того, как сравнение может быть выполнено.

1 голос
/ 28 марта 2014

Как на счет этого?

if (theNumber.isBetween(low, high, IntEx.Bounds.INCLUSIVE_INCLUSIVE))
{
}

с методом расширения следующим образом (проверено):

public static class IntEx
{
    public enum Bounds 
    {
        INCLUSIVE_INCLUSIVE, 
        INCLUSIVE_EXCLUSIVE, 
        EXCLUSIVE_INCLUSIVE, 
        EXCLUSIVE_EXCLUSIVE
    }

    public static bool isBetween(this int theNumber, int low, int high, Bounds boundDef)
    {
        bool result;
        switch (boundDef)
        {
            case Bounds.INCLUSIVE_INCLUSIVE:
                result = ((low <= theNumber) && (theNumber <= high));
                break;
            case Bounds.INCLUSIVE_EXCLUSIVE:
                result = ((low <= theNumber) && (theNumber < high));
                break;
            case Bounds.EXCLUSIVE_INCLUSIVE:
                result = ((low < theNumber) && (theNumber <= high));
                break;
            case Bounds.EXCLUSIVE_EXCLUSIVE:
                result = ((low < theNumber) && (theNumber < high));
                break;
            default:
                throw new System.ArgumentException("Invalid boundary definition argument");
        }
        return result;
    }
}
1 голос
/ 20 сентября 2013

Я бы сделал объект Range, что-то вроде этого:

public class Range<T> where T : IComparable
{
    public T InferiorBoundary{get;private set;}
    public T SuperiorBoundary{get;private set;}

    public Range(T inferiorBoundary, T superiorBoundary)
    {
        InferiorBoundary = inferiorBoundary;
        SuperiorBoundary = superiorBoundary;
    }

    public bool IsWithinBoundaries(T value){
        return InferiorBoundary.CompareTo(value) > 0 && SuperiorBoundary.CompareTo(value) < 0;
    }
}

Тогда вы используете это так:

Range<int> myRange = new Range<int>(1,999);
bool isWithinRange = myRange.IsWithinBoundaries(3);

Таким образом, вы можете использовать его для другого типа.

1 голос
/ 06 июля 2010

Новый поворот в старых любимых:

public bool IsWithinRange(int number, int topOfRange, int bottomOfRange, bool includeBoundaries) {
    if (includeBoundaries)
        return number <= topOfRange && number >= bottomOfRange;
    return number < topOfRange && number > bottomOfRange;
}
0 голосов
/ 07 марта 2019

Вы ищете in [1..100]?Это только Паскаль.

0 голосов
/ 08 октября 2018

Я не знаю, но я использую этот метод:

    public static Boolean isInRange(this Decimal dec, Decimal min, Decimal max, bool includesMin = true, bool includesMax = true ) {

    return (includesMin ? (dec >= min) : (dec > min)) && (includesMax ? (dec <= max) : (dec < max));
}

И вот как я могу это использовать:

    [TestMethod]
    public void IsIntoTheRange()
    {
        decimal dec = 54;

        Boolean result = false;

        result = dec.isInRange(50, 60); //result = True
        Assert.IsTrue(result);

        result = dec.isInRange(55, 60); //result = False
        Assert.IsFalse(result);

        result = dec.isInRange(54, 60); //result = True
        Assert.IsTrue(result);

        result = dec.isInRange(54, 60, false); //result = False
        Assert.IsFalse(result);

        result = dec.isInRange(32, 54, false, false);//result = False
        Assert.IsFalse(result);

        result = dec.isInRange(32, 54, false);//result = True
        Assert.IsTrue(result);
    }
0 голосов
/ 07 декабря 2017

Элегантно, потому что вам не нужно определять, какое из двух граничных значений больше первогоТакже не содержит ветвей.

public static bool InRange(float val, float a, float b)
{
    // Determine if val lies between a and b without first asking which is larger (a or b)
    return ( a <= val & val < b ) | ( b <= val & val < a );
}
...