Определить, попадает ли число в указанный набор диапазонов - PullRequest
18 голосов
/ 16 июля 2009

Я ищу свободный способ определить, попадает ли число в указанный набор диапазонов. Мой текущий код выглядит примерно так:

int x = 500; // Could be any number

if ( ( x > 4199 && x < 6800 ) ||
     ( x > 6999 && x < 8200 ) ||
     ( x > 9999 && x < 10100 ) ||
     ( x > 10999 && x < 11100 ) ||
     ( x > 11999 && x < 12100 ) )
{
    // More awesome code
}

Есть ли лучший способ сделать это?

Ответы [ 8 ]

36 голосов
/ 16 июля 2009

Методы расширения?

bool Between(this int value, int left, int right)
{ 
   return value > left && value < right; 
}

if(x.Between(4199, 6800) || x.Between(6999, 8200) || ...)

Вы также можете сделать этот ужасный хак:

bool Between(this int value, params int[] values)
{
    // Should be even number of items
    Debug.Assert(values.Length % 2 == 0); 

    for(int i = 0; i < values.Length; i += 2)
        if(!value.Between(values[i], values[i + 1])
            return false;

    return true;
}

if(x.Between(4199, 6800, 6999, 8200, ...)

Ужасный хак, улучшено:

class Range
{
    int Left { get; set; }
    int Right { get; set; }

    // Constructors, etc.
}

Range R(int left, int right)
{
    return new Range(left, right)
}

bool Between(this int value, params Range[] ranges)
{
    for(int i = 0; i < ranges.Length; ++i)
        if(value > ranges[i].Left && value < ranges[i].Right)
            return true;

    return false;
}

if(x.Between(R(4199, 6800), R(6999, 8200), ...))

Или, еще лучше (это не позволяет дублировать нижние границы):

bool Between(this int value, Dictionary<int, int> ranges)
{
    // Basically iterate over Key-Value pairs and check if value falls within that range
}

if(x.Between({ { 4199, 6800 }, { 6999, 8200 }, ... }
14 голосов
/ 16 июля 2009

Определите тип Range, затем создайте набор диапазонов и метод расширения, чтобы увидеть, находится ли значение в каком-либо из диапазонов. Затем вместо жесткого кодирования значений вы можете создать коллекцию диапазонов и, возможно, несколько отдельных диапазонов, дав им полезные имена для объяснения , почему они вас интересуют:

static readonly Range InvalidUser = new Range(100, 200);
static readonly Range MilkTooHot = new Range (300, 400);

static readonly IEnumerable<Range> Errors =
    new List<Range> { InvalidUser, MilkTooHot };

...

// Normal LINQ (where Range defines a Contains method)
if (Errors.Any(range => range.Contains(statusCode))
// or (extension method on int)
if (statusCode.InAny(Errors))
// or (extension methods on IEnumerable<Range>)
if (Errors.Any(statusCode))

Вас может заинтересовать универсальный тип Range, который является частью MiscUtil . Он также позволяет выполнять итерации простым способом:

foreach (DateTime date in 19.June(1976).To(25.December(2005)).Step(1.Days()))
{
    // etc
}

(Очевидно, что также используются некоторые методы расширения, связанные с DateTime / TimeSpan, но вы поняли идею.)

7 голосов
/ 16 июля 2009

Лично я предпочитаю метод расширения, предложенный @Anton - но если вы не можете этого сделать и собираетесь придерживаться вашего текущего кода, я думаю, вы могли бы сделать его более читабельным, изменив первый набор условий на каждом строка следующим образом ...

int x = 500; // Could be any number
if ( ( 4199 < x && x < 6800 ) ||
     ( 6999 < x && x < 8200 ) ||
     ( 9999 < x && x < 10100 ) ||
     ( 10999 < x && x < 11100 ) ||
     ( 11999 < x && x < 12100 ) )
{
    // More awesome code
}
3 голосов
/ 16 июля 2009

LINQ подход:

Добавить ссылку:

using System.Linq;

        /// <summary>
        /// Test to see if value is in specified range.
        /// </summary>
        /// <param name="aStart">int</param>
        /// <param name="aEnd">int</param>
        /// <param name="aValueToTest">int</param>
        /// <returns>bool</returns>
        public static bool CheckValueInRange(int aStart, int aEnd, int aValueToTest)
        {
            // check value in range...
            bool ValueInRange = Enumerable.Range(aStart, aEnd).Contains(aValueToTest);
            // return value...
            return ValueInRange;
        }
1 голос
/ 16 июля 2009
class Range { 

    public Range(int x, int y) {
        X = x;
        Y = y;
    }

    public int X { get; set; }
    public int Y { get; set; }
}

var ranges = new List<Range>();
ranges.Add(new Range(4199,6800));
ranges.Add(new Range(6999,8200));
ranges.Add(new Range(9999,10100));
ranges.Add(new Range(10999,11100));
ranges.Add(new Range(11999,12100));

bool inRange = ranges.Count(r => x >= r.X && x <= r.Y) > 0;
//or -- Based on Jons recommendation
bool inRange = ranges.Any(r => x >= r.X && x <= r.Y);
0 голосов
/ 21 июля 2012

В Паскале (Delphi) у вас есть следующий оператор:

if x in [40..80] then
begin
end

Так что, если значение x попадает в этот диапазон, вы выполняете свою команду.Я искал C # эквивалент этого, но не могу найти что-то столь же простое и «элегантное», как это.

Это, если в () тогда оператор принимает строки, байты и т. Д.

0 голосов
/ 16 июля 2009

Попробуйте что-то вроде:

struct Range
{
   public readonly int LowerBound;
   public readonly int UpperBound; 

   public Range( int lower, int upper )
   { LowerBound = lower; UpperBound = upper; }

   public bool IsBetween( int value )
   { return value >= LowerBound && value <= UpperBound; }
}

public void YourMethod( int someValue )
{
   List<Range> ranges = {new Range(4199,6800),new Range(6999,8200),
                         new Range(9999,10100),new Range(10999,11100),
                         new Range(11999,12100)};

   if( ranges.Any( x => x.IsBetween( someValue ) )
   {
      // your awesome code...
   }
}
0 голосов
/ 16 июля 2009

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

if ( x>max_lower && x <min_upper)
{
    // More awesome code

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...