Как элегантно проверить, находится ли число в пределах диапазона?

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

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

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

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

Ответ 1

Есть много вариантов:

int x = 30;
if (Enumerable.Range(1,100).Contains(x))
    //true

if (x >= 1 && x <= 100)
    //true

Кроме того, ознакомьтесь с этим сообщением SO для параметров регулярного выражения.

Ответ 2

Вы имеете в виду?

if(number >= 1 && number <= 100)

или

bool TestRange (int numberToCheck, int bottom, int top)
{
  return (numberToCheck >= bottom && numberToCheck <= top);
}

Ответ 3

Чтобы добавить сюда шум, вы можете создать метод расширения:

public static bool IsWithin(this int value, int minimum, int maximum)
{
    return value >= minimum && value <= maximum;
}

Что бы вы сделали что-то вроде...

int val = 15;

bool foo = val.IsWithin(5,20);

Говоря это, это кажется глупым делом, когда сам чек - это только одна строка.

Ответ 4

Как говорили другие, используйте простой if.

Вы должны подумать о заказе.

например

1 <= x && x <= 100

легче читать, чем

x >= 1 && x <= 100

Ответ 5

Вы можете уменьшить количество сравнений с двух до одного, используя математические вычисления. Идея состоит в том, что один из двух факторов становится отрицательным, если число лежит за пределами диапазона, и ноль, если число равно одной из границ:

Если границы включены:

(x - 1) * (100 - x) >= 0

или

(x - min) * (max - x) >= 0

Если границы являются исключительными:

(x - 1) * (100 - x) > 0

или

(x - min) * (max - x) > 0

Однако в рабочем коде я бы просто написал 1 < x && x < 100, это легче понять.

Ответ 6

С небольшим злоупотреблением методом расширения мы можем получить следующее "элегантное" решение:

using System;

namespace Elegant {
    public class Range {
        public int Lower { get; set; }
        public int Upper { get; set; }
    }

    public static class Ext {
        public static Range To(this int lower, int upper) {
            return new Range { Lower = lower, Upper = upper };
        }

        public static bool In(this int n, Range r) {
            return n >= r.Lower && n <= r.Upper;
        }
    }

    class Program {
        static void Main() {
            int x = 55;
            if (x.In(1.To(100)))
                Console.WriteLine("it in range! elegantly!");
        }
    }
}

Ответ 7

Я предлагаю следующее:

public static bool IsWithin<T>(this T value, T minimum, T maximum) where T : IComparable<T> {
    if (value.CompareTo(minimum) < 0)
       return false;
    if (value.CompareTo(maximum) > 0)
       return false;
    return true;
}

Примеры:

45.IsWithin(32, 89)
true
87.2.IsWithin(87.1, 87.15)
false
87.2.IsWithin(87.1, 87.25)
true

и, конечно, с переменными:

myvalue.IsWithin(min, max)

Легко читается (приближается к человеческому языку) и работает с любым сопоставимым типом (целочисленным, двойным, пользовательским типам...).

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

Ответ 8

Если это случайно, просто if - это все, что вам нужно. Если это происходит во многих местах, вы можете рассмотреть эти два вопроса:

  • PostSharp. Украшайте методы атрибутами, которые "вводят" код в метод после компиляции. Я не знаю точно, но могу представить, что это можно использовать для этого.

Что-то вроде:

[Between("parameter", 0, 100)]
public void Foo(int parameter)
{
}
  • Кодовые контракты. Имеет то преимущество, что ограничения можно проверять во время компиляции, путем статической проверки вашего кода и мест, в которых используется ваш код.

Ответ 9

if (value > 1 && value < 100)
{
    // do work
}
else
{
    // handle outside of range logic
}

Ответ 10

Использование выражения && для объединения двух сравнений - это просто самый элегантный способ сделать это. Если вы попытаетесь использовать причудливые методы расширения и т.д., Вы столкнетесь с вопросом о том, включать ли верхнюю границу, нижнюю границу или и то, и другое. Когда вы начинаете добавлять дополнительные переменные или меняете имена расширений, чтобы указать, что включено, ваш код становится длиннее и труднее читать (для подавляющего большинства программистов). Кроме того, такие инструменты, как Resharper, будут предупреждать вас, если ваше сравнение не имеет смысла (number > 100 && number < 1), что они не будут делать, если вы используете метод ( "i.IsBetween(100, 1)" ).

Единственный другой комментарий, который я бы сделал, заключается в том, что если вы проверяете ввод данных с намерением выбросить исключение, вы должны рассмотреть возможность использования кодовых контрактов:

Contract.Requires(number > 1 && number < 100)

Это более элегантно, чем if(...) throw new Exception(...), и вы даже можете получать предупреждения о компиляции, если кто-то пытается вызвать ваш метод, не гарантируя, что число сначала находится в границах.

Ответ 11

Если вы хотите написать больше кода, чем просто, возможно, вы можете: Создайте метод расширения с именем 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), которая встроена в эту среду; на практике вам обычно приходится тщательно разрабатывать свои собственные концепции того, что означает сравнение для компьютерных представлений реальных чисел, для типа ситуации, которую вы разрабатываете. (Даже не упоминайте, что если вы делаете что-то вроде, возможно, контроллера, ПИД-регулятора или тому подобного, вся проблема становится центральной и очень сложной, она становится природой проекта.) Ни в коем случае не является OP вопрос здесь тривиальный или несущественный вопрос.

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;
    }

Ответ 12

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

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);

Ответ 13

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

public bool IsWithinRange(int number, int topOfRange, int bottomOfRange, bool includeBoundaries) {
    if (includeBoundaries)
        return number <= topOfRange && number >= bottomOfRange;
    return number < topOfRange && number > bottomOfRange;
}

Ответ 14

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

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

Ответ 15

Я бы сделал объект 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);

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

Ответ 16

Как насчет чего-то подобного?

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;
    }
}

Ответ 17

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);
    }
}

Usage

Usage double numberToBeChecked = 7;

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

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

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

Ответ 18

Я бы пошел с более простой версией:

if(Enumerable.Range(1,100).Contains(intInQuestion)) { ...DoStuff; }

Ответ 19

Я искал элегантный способ сделать это, где границы могут быть переключены (т.е. не уверены, в каком порядке находятся значения).

Это будет работать только в более новых версиях С#, где?: существует

bool ValueWithinBounds(float val, float bounds1, float bounds2)
{
    return bounds1 >= bounds2 ?
      val <= bounds1 && val >= bounds2 : 
      val <= bounds2 && val >= bounds1;
}

Очевидно, вы могли бы изменить знаки = там, где есть ваши цели. Мог бы получить фантазии с типом литья тоже. Я просто нуждался в возврате float в пределах (или равных)

Ответ 20

Когда вы проверяете, находится ли "номер" в диапазоне, вы должны быть понятны в том, что вы имеете в виду, и что означает два числа, равные среднему значению? В общем, вы должны перенести все числа с плавающей запятой в так называемый "шарик эпсилон", это делается, выбирая небольшое значение и говоря, что если два значения близки, они одно и то же.

    private double _epsilon = 10E-9;
    /// <summary>
    /// Checks if the distance between two doubles is within an epsilon.
    /// In general this should be used for determining equality between doubles.
    /// </summary>
    /// <param name="x0">The orgin of intrest</param>
    /// <param name="x"> The point of intrest</param>
    /// <param name="epsilon">The minimum distance between the points</param>
    /// <returns>Returns true iff x  in (x0-epsilon, x0+epsilon)</returns>
    public static bool IsInNeghborhood(double x0, double x, double epsilon) => Abs(x0 - x) < epsilon;

    public static bool AreEqual(double v0, double v1) => IsInNeghborhood(v0, v1, _epsilon);

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

    public enum BoundType
    {
        Open,
        Closed,
        OpenClosed,
        ClosedOpen
    }

Следующий метод:

    public static bool InRange(double value, double upperBound, double lowerBound, BoundType bound = BoundType.Open)
    {
        bool inside = value < upperBound && value > lowerBound;
        switch (bound)
        {
            case BoundType.Open:
                return inside;
            case BoundType.Closed:
                return inside || AreEqual(value, upperBound) || AreEqual(value, lowerBound); 
            case BoundType.OpenClosed:
                return inside || AreEqual(value, upperBound);
            case BoundType.ClosedOpen:
                return inside || AreEqual(value, lowerBound);
            default:
                throw new System.NotImplementedException("You forgot to do something");
        }
    }

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

Ответ 21

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

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 );
}

Ответ 22

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

    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);
    }

Ответ 23

Вот некоторые методы расширения, которые могут помочь

  public static bool IsInRange<T>(this T value, T min, T max)
where T : System.IComparable<T>
    {
        return value.IsGreaterThenOrEqualTo(min) && value.IsLessThenOrEqualTo(max);
    }


    public static bool IsLessThenOrEqualTo<T>(this T value, T other)
         where T : System.IComparable<T>
    {
        var result = value.CompareTo(other);
        return result == -1 || result == 0;
    }


    public static bool IsGreaterThenOrEqualTo<T>(this T value, T other)
         where T : System.IComparable<T>
    {
        var result = value.CompareTo(other);
        return result == 1 || result == 0;
    }

Ответ 24

Если вас беспокоит комментарий @Daap о принятом ответе, и вы можете передать значение только один раз, попробуйте один из следующих

bool TestRangeDistance (int numberToCheck, int bottom, int distance)
{
  return (numberToCheck >= bottom && numberToCheck <= bottom+distance);
}

//var t = TestRangeDistance(10, somelist.Count()-5, 10);

или

bool TestRangeMargin (int numberToCheck, int target, int margin)
{
  return (numberToCheck >= target-margin && numberToCheck <= target+margin);
}

//var t = TestRangeMargin(10, somelist.Count(), 5);

Ответ 25

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