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

157

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

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

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

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

Серхио Тапиа
источник
23
Re: Ваше "правка" - просто элегантно . Лично я нахожу утверждение if более элегантным, чем любое нестандартное средство выполнения этой проверки ...
Рид Копси,
4
«Все должно быть сделано как можно проще, но не проще». - Альберт Эйнштейн
КорсиКа
3
@ Sergio: я не чувствую себя педантичным. Я чувствую, что люди часто злоупотребляют методами расширения и другими инструментами в языке, чтобы заменить простые вещи. Существуют сотни способов сравнить два значения типа int, но использование чего-либо, кроме более очевидного, является плохим выбором, IMO.
Рид Копси
3
@Sergio: Тогда я не вижу смысла в этом вопросе;)
Рид Копси
6
@ Серджио: если ifне "барокко", не исправляйте это.
StriplingWarrior

Ответы:

154

Вариантов много:

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

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

Кроме того, проверьте этот SO пост для опций регулярных выражений.

Дастин Лэйн
источник
334
Enumerable.Range должен сначала сгенерировать перечислимые целые числа, а затем перебрать каждый элемент, чтобы найти его. Это ужасная идея, и производительность по сравнению с проверкой значения кардинально отличается. Я думаю, что мы должны принять девиз, просто потому что LINQ Extensions - это круто, это не значит, что они должны использоваться для всего.
Мэтью Эбботт
14
@Matthew: stackoverflow.com/questions/777400/…
Адам Робинсон
15
Я согласен, что это ужасная идея с точки зрения производительности, но ОП хочет чего-то более причудливого, чем ifзаявление. Это, безусловно, делает это ...;)
Тим Кокер
10
Стоит отметить, что вторым параметром является не «стоп», а «счет». Так, например, Enumerable.Range (150, 300) .Contains (400) вернет true.
Шатур
5
Пожалуйста, не используйте этот ответ . Это будет иметь ужасную производительность, если ваши диапазоны довольно велики. Пожалуйста, смотрите ответ @ olivier-jacot-descombes
Аарон Хадон
95

Ты имеешь ввиду?

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

или

bool TestRange (int numberToCheck, int bottom, int top)
{
  return (numberToCheck >= bottom && numberToCheck <= top);
}
kemiller2002
источник
1
Вам не нужно там "есть" ... Это не скомпилируется. (В противном случае, я согласен на 100%)
Рид Копси
4
@Ben, просто подожди, пока я не попытаюсь запатентовать это тоже :)
kemiller2002
Я думаю, что это самое солидное решение, но не то, что элегантно ищет спрашивающий, не так ли?
Кевин Прост
Единственное, что я хотел бы изменить, это добавить ключевое слово static в метод. ;-)
Роберт С.
Необходимы граничные флаги, т.е. InRange (число, lowerBound, LOWER_IS_INCLUSIVE, Upperbound, UPPER_IS_EXCLUSIVE), чтобы допускать <vs <=. Я написал это, намереваясь быть странным, но теперь, когда я думаю об этом, флаги на самом деле побудят вызывающего абонента прояснить их спецификацию.
Уильям Т. Маллард
56

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

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

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

Адам Робинсон
источник
1
@Ben: Я остановился на предмете, который говорит «в пределах диапазона» (который я не считаю неоднозначным в этом отношении), но вы правы в том, что тело вопроса говорит «между 1 и 100» (что конечно, неоднозначно).
Адам Робинсон
48

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

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

например

1 <= x && x <= 100

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

x >= 1 && x <= 100
Эсбен Сков Педерсен
источник
19
«Легче» в глазах смотрящего. Лично я предпочитаю, чтобы рассматриваемая переменная была слева, а константа или переменная не обсуждалась справа.
Адам Робинсон
15
В Perl 6 вы бы написали 1 <= x <= 100.
Джордао
2
Изначально порядок числовых линий самый ясный, но вы можете тренировать свой ум / ум для других заказов. Конкретно - мне всегда нравится размещать постоянную слева. Если вы сделаете это, компилятор сообщит вам, когда вы набрали =вместо ==. Это не помогает с реляционными операторами неравенства - но легко привыкнуть к его последовательному использованию.
Давидбак
1
Я просто хочу добавить, что это решение бесполезно в любом случае. Рассмотрим xсложный вызов функции или трудоемкое Linq-выражение. В этом случае вы бы сделали это дважды, что не очень хорошая вещь. Конечно, вы должны сохранить значение во временной локальной переменной, но в некоторых случаях (например, в выражениях else-if-if) вы хотите вызывать функции только после сбоя if, if или else-if. С временными переменными вы должны вызывать их в любом случае раньше. Метод расширения (упомянутый в других ответах) является лучшим решением imho в этих случаях.
Роберт С.
4
Мне также нравится порядок числовых линий, а также для теста дополнения, например, x <10 || 20 <х. Для меня это кричит "х вне диапазона 10 - 20".
Уильям Т. Маллард
44

В производственном коде я бы просто написал

1 <= x && x <= 100

Это легко понять и очень легко прочитать.


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

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

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

или

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

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

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

или

(x - min) * (max - x) > 0
Оливье Жако-Дескомб
источник
3
По моим стандартам это, безусловно, самое элегантное решение, интересное, что для меня это также кажется, что оно работает несколько быстрее, чем проверка обоих выражений, что говорит о том, что оно также кажется более непоследовательным (скорость, кажется, меняется больше), было бы интересно посмотреть если есть какие-либо исследования, которые делают быстрее.
Томас Линдвалл
3
Протестировал ваше решение на javascript и его точность с числами с плавающей запятой до 14 знаков после запятой. Это очень хороший фрагмент кода. Если бы я мог, тебя бы трижды
поддержали
4
Тем не менее, есть небольшая проблема, если задействованы большие положительные числа, она может переполниться! XD Возможно, вы захотите помнить об этом при написании кода.
BrainStorm.exe
2
Вопрос требует элегантности и поэтому является скорее академическим, чем практическим значением. Лично я бы просто использовал простой 1 < x && x < 100продуктивный код. Это легче понять.
Оливье Жако-Дескомб
1
Для тех, кто обеспокоен производительностью, 1 < x & x < 100(без && короткого замыкания) указывает компилятору, что он всегда может оценить x < 100независимо от результата 1 < x. Как ни странно (из-за предсказания ветвлений) всегда проще выполнить эту простую операцию, чем иногда ее пропустить.
Том Лейс
23

Я предлагаю это:

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)

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

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

Антон М
источник
3
я бы упростил еще больше, используя слово между и имея логический флаг, чтобы определить, является ли он включительным или нет
Бен
Хорошо. Это легко понять. Я изменил имя IsInRange. I'm not that keen on Ben's inclusive boolean as that requires a few more brain cycles. It has the advantage that it can be used in any class that that implements IComparer. This is in my Extensions now along with LiesWithin / LiesInside. Just can't decide which. NotOutside будет работать, но я не люблю негативные условия
Paulustrious
21

Немного злоупотребив методом расширения, мы можем получить следующее «элегантное» решение:

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's in range! elegantly!");
        }
    }
}
Ферруччо
источник
Понравилось решение! Btw для поддержки включительно, создать enum Inclusiveсо значениями: Lower, Upper, All. И передать для Inфункции одного дополнительного параметра типа enum Inclusiveс Значением по умолчанию Inclusive.All, обновление Toтела функции ручки All, Lower, Upperзначение :)
Никита
7

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

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

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

[Between("parameter", 0, 100)]
public void Foo(int parameter)
{
}
  • Кодекс контрактов . Преимущество заключается в том, что ограничения могут быть проверены во время компиляции, путем статической проверки вашего кода и мест, в которых он используется.
JulianR
источник
+1 для кодовых контрактов; он специфичен для проверки параметра, но это частый случай использования, и статическая проверка потенциально может быть чрезвычайно полезной.
Дэн Брайант
5
if (value > 1 && value < 100)
{
    // do work
}
else
{
    // handle outside of range logic
}
Ник Ларсен
источник
5

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

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

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

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

StriplingWarrior
источник
2
К вашему сведению, статический анализатор контрактов более удачен, когда ограничения нижней и верхней границ разделены на отдельные операторы Require.
Дэн Брайант
Спасибо Дэну Брайанту, это именно то, что я искал здесь. Не могу найти много материала о предложениях по стилю условий для Требований и других связанных с этим методов Кодового контракта.
jpierson
2

Если вы хотите написать больше кода, чем простой 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;
    }
Тони
источник
1
Замените if и else наreturn (value >= Min && value <= Max);
AeroX
элегантный способ написать сравнение - «в логическом порядке ...» if (Min <= value && value <= Max). Это намного красивее.
Толстяк
2
Далее по этому вопросу, это так удивительно, что никто не упомянул центральную проблему в любом реальном проекте (особенно, если вы игровой инженер) , что вам приходится иметь дело с проблемой аппроксимации . В любом реальном программном обеспечении вы, по сути, никогда не «просто проводите сравнение» (будь то равенство или <,>), вы должны учитывать и решать проблему ошибок, в зависимости от ситуации. Я отредактировал в приложении к этому ответу (единственный правильный ответ здесь!), Так как больше никаких ответов не разрешено.
Толстяк
Спасибо за это замечание и дополнение.
Тони
2

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

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);
Оливер
источник
2

РЕДАКТИРОВАТЬ: Новый ответ при условии. Я только начинал использовать C #, когда написал первый ответ на этот вопрос, и теперь я понимаю, что мое «решение» было / наивно и неэффективно.

Мой оригинальный ответ: я бы пошел с более простой версией:

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

Лучший путь

Поскольку я не видел ни одного другого более эффективного решения (по крайней мере, согласно моим тестам), я попробую еще раз.

Новый и лучший способ, который также работает с отрицательными диапазонами :

// Returns true if x is in range [min..max], else false 
bool inRange(int x, int min=1, int max=100) => ((x - max)*(x - min) <= 0);

Это может использоваться как с положительным, так и с отрицательным диапазоном, а по умолчанию используется диапазон

1..100 (включительно) и использует xв качестве номера для проверки, за которым следует необязательный диапазон, определяемый minи max.

Добавление примеров для хорошей меры

Пример 1:

// Returns true if x is in range [min..max], else false 
bool inRange(int x, int min=1, int max=100) => ((x - max)*(x - min) <= 0);

Console.WriteLine(inRange(25));
Console.WriteLine(inRange(1));
Console.WriteLine(inRange(100));
Console.WriteLine(inRange(25, 30, 150));
Console.WriteLine(inRange(-25, -50, 0));

Возвращает:

True
True
True
False
True

Пример 2. Использование списка случайных чисел от 1 до 150

// Returns true if x is in range [min..max], else false 
bool inRange(int x, int min=1, int max=100) => ((x - max)*(x - min) <= 0);

// Generate 100000 ints between 1 and 150
var intsToCheck = new List<int>();
var randGen = new Random();
for(int i = 0; i < 100000; ++i){
    intsToCheck.Add(randGen.Next(150) + 1);
}

var counter = 0;
foreach(int n in intsToCheck) {
    if(inRange(n)) ++counter;
}

Console.WriteLine("{0} ints found in range 1..100", counter);

Возвращает:

66660 ints found in range 1..100

Время выполнения: 0,016 секунды

cseder
источник
Да, я комментирую комментарий к моему ответу от 2013 года :) @RyanTheLeach: Чем мой ответ на этот вопрос отличается от ныне «принятого» ответа? Я понимаю, что это не самый эффективный обход, но «ужасный»? Насколько плохо может быть выделение и циклическое прохождение через 100 дюймов? В 1950 это, вероятно, не было принято обществом, но ...
cseder
@RyanTheLeach Я не виню вас ... Я обновил свой ответ, поэтому, если вы знаете о решении, которое является еще более эффективным, пожалуйста, уточните!
cseder
1
Я удалил свои комментарии, поскольку они больше не стоят. Спасибо за исправление, кажется, все в порядке.
Райан Лич
1

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

public bool IsWithinRange(int number, int topOfRange, int bottomOfRange, bool includeBoundaries) {
    if (includeBoundaries)
        return number <= topOfRange && number >= bottomOfRange;
    return number < topOfRange && number > bottomOfRange;
}
Бен Хоффштейн
источник
3
На самом деле есть четыре случая: включительно / включительно, включительно / эксклюзивно, эксклюзивно / включено и эксклюзивно / эксклюзивно.
Уильям Т. Маллард
1

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

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

Supercat
источник
1

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

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

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

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

IEatBagels
источник
Ваш Rangeобъект должен использовать CompareToметод для сравнения элементов, а не <оператор.
Servy
Вы правы, хотя, если вы реализуете IComparable, вам также следует переопределить операторы (по крайней мере, так говорит мой анализ кода VS), то есть <будет работать. Хотя я могу ошибаться, у меня не так много опыта, и это мой первый ответ на SO
IEatBagels
Нет, ваш компилятор не скажет, что это работает. Это не скомпилируется. Для объекта вполне разумно реализовать IComparableи не перегружать <оператора.
Servy
1

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

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

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

rahicks
источник
1
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);

İBRAHİM GAZALOĞLU
источник
1

Если вас беспокоит комментарий @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);
Хьюго Делсинг
источник
1

Что касается элегантности, то самая близкая вещь к математической записи ( a <= x <= b ) немного улучшает читабельность:

public static bool IsBetween(this int value, int min, int max)
{
    return min <= value && value <= max;
}

Для дальнейшей иллюстрации:

public static bool IsOutside(this int value, int min, int max)
{
    return value < min || max < value;
}
Гектор-J-Rivas
источник
0

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

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

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

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

Kalikovision
источник
0

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

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 );
}
Том Лейс
источник
& + | являются побитовыми операторами
nelsontruran
0

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

    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);
    }
user8790965
источник
Пожалуйста, предоставьте пример использования ниже блока кода, это поможет ОП узнать, соответствует ли он его цели
Габриэль Бальса Канту
0

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

  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;
    }
Ханан
источник
0

Если это проверка параметров метода, ни одно из решений не выдает исключение ArgumentOutOfRangeException и позволяет легко / правильно настроить включающие / исключающие минимальные / максимальные значения.

Используйте как это

public void Start(int pos)
{
    pos.CheckRange(nameof(pos), min: 0);

    if (pos.IsInRange(max: 100, maxInclusive: false))
    {
        // ...
    }
}

Я просто написал эти прекрасные функции. Он также имеет то преимущество, что не имеет разветвления (единственное if) для допустимых значений. Самое сложное - создать правильные сообщения об исключениях.

/// <summary>
/// Returns whether specified value is in valid range.
/// </summary>
/// <typeparam name="T">The type of data to validate.</typeparam>
/// <param name="value">The value to validate.</param>
/// <param name="min">The minimum valid value.</param>
/// <param name="minInclusive">Whether the minimum value is valid.</param>
/// <param name="max">The maximum valid value.</param>
/// <param name="maxInclusive">Whether the maximum value is valid.</param>
/// <returns>Whether the value is within range.</returns>
public static bool IsInRange<T>(this T value, T? min = null, bool minInclusive = true, T? max = null, bool maxInclusive = true)
    where T : struct, IComparable<T>
{
    var minValid = min == null || (minInclusive && value.CompareTo(min.Value) >= 0) || (!minInclusive && value.CompareTo(min.Value) > 0);
    var maxValid = max == null || (maxInclusive && value.CompareTo(max.Value) <= 0) || (!maxInclusive && value.CompareTo(max.Value) < 0);
    return minValid && maxValid;
}

/// <summary>
/// Validates whether specified value is in valid range, and throws an exception if out of range.
/// </summary>
/// <typeparam name="T">The type of data to validate.</typeparam>
/// <param name="value">The value to validate.</param>
/// <param name="name">The name of the parameter.</param>
/// <param name="min">The minimum valid value.</param>
/// <param name="minInclusive">Whether the minimum value is valid.</param>
/// <param name="max">The maximum valid value.</param>
/// <param name="maxInclusive">Whether the maximum value is valid.</param>
/// <returns>The value if valid.</returns>
public static T CheckRange<T>(this T value, string name, T? min = null, bool minInclusive = true, T? max = null, bool maxInclusive = true)
where T : struct, IComparable<T>
{
    if (!value.IsInRange(min, minInclusive, max, maxInclusive))
    {
        if (min.HasValue && minInclusive && max.HasValue && maxInclusive)
        {
            var message = "{0} must be between {1} and {2}.";
            throw new ArgumentOutOfRangeException(name, value, message.FormatInvariant(name, min, max));
        }
        else
        {
            var messageMin = min.HasValue ? GetOpText(true, minInclusive).FormatInvariant(min) : null;
            var messageMax = max.HasValue ? GetOpText(false, maxInclusive).FormatInvariant(max) : null;
            var message = (messageMin != null && messageMax != null) ?
                "{0} must be {1} and {2}." :
                "{0} must be {1}.";
            throw new ArgumentOutOfRangeException(name, value, message.FormatInvariant(name, messageMin ?? messageMax, messageMax));
        }
    }
    return value;
}

private static string GetOpText(bool greaterThan, bool inclusive)
{
    return (greaterThan && inclusive) ? "greater than or equal to {0}" :
        greaterThan ? "greater than {0}" :
        inclusive ? "less than or equal to {0}" :
        "less than {0}";
}

public static string FormatInvariant(this string format, params object?[] args) => string.Format(CultureInfo.InvariantCulture, format, args);
Этьен Чарланд
источник
-2

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

Поллукс
источник
2
Не правда, это не только Паскаль. Многие современные языки имеют такие особенности. В Kotlin, например, это называется «Pattern Matching». Пример when (number) { in 0..9 -> println("1 digit") in 10..99 -> println("2 digits") in 100..999 -> println("3 digits") }
this.myself