2009-07-16 3 views
18

Je suis à la recherche d'un moyen simple de déterminer si un nombre se situe dans un ensemble de plages spécifié. Mon code actuel ressemble à ceci:Déterminer si un nombre se situe dans un ensemble spécifié de plages

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 
} 

Y a-t-il une meilleure façon de procéder?

+2

Mieux dans quel sens? Cela semble assez raisonnable –

+3

@Michael: Je crois que Steve demande une approche plus éloquente. –

+1

Je pense que l'exemple que vous avez fourni est le meilleur moyen (honnête et compréhensible) d'être honnête! – ThePower

Répondre

36

Méthodes d'extension?

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

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

Vous pouvez aussi faire ce hack terrible:

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

bidouille Awful, amélioré:

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

Ou, mieux encore (cela ne permet pas de bornes en double plus bas):

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

Je voudrais * certainement * rendre Range immutable. Mutabilité craint pour des choses comme ça ... –

+0

Whoa! _Formatting_ dans ** commentaires **! @Jon Oui, vous avez absolument raison. –

14

Définir un type de plage, puis cr Avoir un ensemble de plages et une méthode d'extension pour voir si une valeur se trouve dans l'une des plages. Ensuite, au lieu de coder en dur les valeurs, vous pouvez créer une collection de gammes et peut-être quelques gammes individuelles, en leur donnant des noms utiles pour expliquer pourquoi vous êtes intéressé par eux

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

Vous pourriez être intéressé par le type générique Range qui fait partie de MiscUtil. Elle permet d'itération d'une manière simple et:

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

(Il est évident que ça en utilisant aussi des méthodes d'extension liés à TimeSpan DateTime /, mais vous voyez l'idée.)

0

si vous devez parcourir la paires de valeurs à un moment donné, je vous suggère de capturer la valeur inférieure maximale et la valeur supérieure minimum comme vous le faites dans les variables et faites:

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

} 
+0

et si ce n'est pas impossible? –

+0

@ArsenMkrt, a déclaré _what si ce n'est pas impossible? _, Pas impossible = possible? Je ne suis pas sûr de ce que vous demandez –

0

Essayez quelque chose comme:

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

Il est généralement préférable d'utiliser Any() que Count (...)> 0 car il peut alors s'arrêter dès qu'il trouve une correspondance. –

+0

Merci pour la suggestion, je vais ajouter cela à la poste. – Bob

7

Personnellement, je préfère la méthode d'extension suggérée par @Anton - mais si vous ne pouvez pas le faire, et allez coller avec votre code actuel, je pense que vous pouvez le rendre plus lisible en inversant le premier jeu des conditions sur chaque ligne comme suit ...

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

Cela le rend beaucoup plus lisible. C'est indiqué dans Code Complete et j'utilise ce style depuis. – Carra

3

approche LINQ:

Ajouter la référence:

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 fonctionne uniquement à la limite inférieure de la plage. Puisque le deuxième paramètre de Enumerable.Range est le nombre de valeurs à ajouter à la séquence (au lieu de la fin de la plage), les valeurs supérieures à la limite supérieure seront toujours signalées comme étant dans la plage. Exemple: CheckValueInRange (4199,6800,6801) renvoie True, bien que 6801 soit en dehors de la limite supérieure désirée car Enumerable.Range (4199,6800) renvoie une plage de nombres de 4199 à 10998. –

+0

Cette approche est très mauvaise pour les performances, car le tableau sera alloué avec la longueur de la plage et LINQ essaiera de l'itérer pour trouver votre valeur de test. –

-1

Dans Pascal (Delphi) vous avez fait la déclaration suivante:

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

Donc, si une valeur x se situe dans cette plage vous exécutez votre commande. Je cherchais l'équivalent en C# mais je n'arrive pas à trouver quelque chose d'aussi simple et «élégant» que ça.

Cette instruction if in() then accepte les chaînes, les octets, etc.

+3

Ceci est une question C#. –

+0

Pas vraiment pertinent. – Mana