2009-07-16 11 views
18

Sto cercando un metodo fluido per determinare se un numero rientra in un gruppo specifico di intervalli. Il mio codice attuale è simile al seguente:Determinare se un numero rientra in un gruppo specificato di intervalli

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 
} 

Esiste un modo migliore per farlo?

+2

Meglio in che senso? Questo sembra abbastanza ragionevole –

+3

@ Michael: Credo che Steve stia chiedendo un approccio più eloquente. –

+1

Penso che l'esempio che hai fornito sia il modo migliore (bello e comprensibile) per essere onesto! – ThePower

risposta

36

Metodi di estensione?

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

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

Si può anche fare questo hack terribile:

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, ...) 

mod Awful, migliorato:

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), ...)) 

O, meglio ancora (questo non consente limiti duplicato inferiori):

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 }, ... } 
+4

Vorrei * sicuramente * rendere la gamma immutabile. La mutabilità fa schifo per cose del genere ... –

+0

Whoa! _Formattazione_ in ** commenti **! @ Sì, hai assolutamente ragione. –

14

Definire un tipo di intervallo, quindi cr mangia un set di intervalli e un metodo di estensione per vedere se un valore si trova in uno qualsiasi degli intervalli. Poi, invece di codificare i valori, è possibile creare un insieme di catene e forse alcuni singoli campi, dando loro nomi utili per spiegare il motivo per cui siete interessati a loro:

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

si può essere interessati a il tipo generico Range che fa parte di MiscUtil. Esso consente per l'iterazione in modo semplice così:

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

(Ovviamente questo è anche l'utilizzo di alcuni metodi di estensione DateTime/TimeSpan-correlati, ma si ottiene l'idea.)

0

se avete bisogno di iterare il coppie di valori a un certo punto, mi suggeriscono di acquisire il valore massimo inferiore e il valore superiore minimo, come si fa in variabili e fare:

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

} 
+0

cosa succede se non è impossibile? –

+0

@ArsenMkrt, ha detto _che se ciò non è impossibile? _, Non impossibile = possibile? Non sono sicuro di cosa stai chiedendo –

0

Prova qualcosa di simile:

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... 
    } 
} 
1
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); 
+1

In genere è preferibile utilizzare Any() di Count (...)> 0, in quanto può arrestarsi non appena viene trovata una corrispondenza. –

+0

Grazie per il suggerimento, lo aggiungerò al post. – Bob

7

Io personalmente preferisco il metodo di estensione suggerito da @Anton - ma se non si può fare, e sta andando a bastone con il codice corrente, penso che si potrebbe rendere più leggibile invertendo il primo set delle condizioni su ogni riga come segue ...

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 
} 
+0

Questo lo rende molto più leggibile. È indicato nel codice completo e da allora uso questo stile. – Carra

3

approccio LINQ:

aggiungere il riferimento:

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

CheckValueInRange funziona solo al limite inferiore dell'intervallo. Poiché il secondo parametro di Enumerable.Range è il numero di valori da aggiungere alla sequenza (anziché la fine dell'intervallo), i valori superiori al limite superiore verranno comunque riportati come nell'intervallo. Esempio: CheckValueInRange (4199,6800,6801) restituisce True, nonostante 6801 sia all'esterno del limite superiore desiderato poiché Enumerable.Range (4199,6800) restituisce un intervallo di numeri da 4199 a 10998. –

+0

Questo approccio è molto negativo per le prestazioni, poiché l'array verrà assegnato con una lunghezza dell'intervallo e LINQ proverà a eseguirne l'iter per trovare il valore del test. –

-1

In Pascal (Delphi) si ha la seguente dichiarazione:

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

Quindi, se un valore x cade in tale intervallo si esegue il comando. Stavo cercando l'equivalente di C# ma non riesco a trovare qualcosa di così semplice e 'elegante' come questo.

Questo if in() quindi istruzione accetta stringhe, byte, ecc.

+3

Questa è una domanda C#. –

+0

Non molto pertinente. – Mana

Problemi correlati