Comment vérifier avec élégance si un nombre se trouve dans une plage?

157

Comment puis-je faire cela avec élégance avec C # et .NET 3.5 / 4?

Par exemple, un nombre peut être compris entre 1 et 100.

Je sais qu'un simple si suffirait; mais le mot clé de cette question est l'élégance. C'est pour mon projet de jouet pas pour la production.

Ces questions ne concernaient pas la vitesse, mais la beauté du code. Arrêtez de parler d'efficacité et autres; souvenez-vous que vous prêchez à la chorale.

Sergio Tapia
la source
23
Re: Votre "éditer" - simple est élégant . Personnellement, je trouve la déclaration if plus élégante que tout moyen non standard de faire cette vérification ...
Reed Copsey
4
"Tout doit être rendu aussi simple que possible, mais pas plus simple." - Albert Einstein
corsiKa
3
@Sergio: Je n'ai pas l'impression d'être pédant. J'ai le sentiment que les gens abusent souvent des méthodes d'extension et d'autres outils dans la langue pour remplacer des choses déjà simples. Il existe des centaines de façons de comparer deux valeurs int, mais utiliser autre chose que le plus évident est un mauvais choix, IMO.
Reed Copsey
3
@Sergio: Je suppose, alors, je ne vois pas le but de la question;)
Reed Copsey
6
@Sergio: si ce ifn'est pas "baroque", ne le répare pas.
StriplingWarrior

Réponses:

154

Il existe de nombreuses options:

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

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

Consultez également cet article SO pour les options de regex.

Dustin Laine
la source
334
Enumerable.Range doit d'abord générer l'énumérable des entiers, puis effectuer une boucle sur chaque élément pour le trouver. C'est une idée terrible et les performances par rapport à la vérification d'une valeur sont radicalement différentes. Je pense que nous devrions adopter une devise, ce n'est pas parce que les extensions LINQ sont cool qu'elles doivent être utilisées pour tout.
Matthew Abbott
14
@Matthew: stackoverflow.com/questions/777400/…
Adam Robinson
15
Je suis d'accord que c'est une idée terrible en termes de performances, mais le PO veut quelque chose de plus sophistiqué qu'une ifdéclaration. Cela accomplit certainement cela ...;)
Tim Coker
10
Il convient de noter que le deuxième paramètre n'est pas "stop", mais "count". Ainsi, par exemple, Enumerable.Range (150, 300) .Contains (400) retournera true.
Shathur
5
Veuillez ne pas utiliser cette réponse . Il aura des performances épouvantables si vos plages sont assez grandes. S'il vous plaît voir la réponse par @ olivier-jacot-descombes
Aaron Hudon
95

Tu veux dire?

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

ou

bool TestRange (int numberToCheck, int bottom, int top)
{
  return (numberToCheck >= bottom && numberToCheck <= top);
}
kemiller2002
la source
1
Vous n'avez pas besoin de "est" là-dedans ... Cela ne compilera pas. (Sinon, je suis d'accord à 100%)
Reed Copsey
4
@Ben, attendez que j'essaye de le breveter aussi :)
kemiller2002
Je pense que c'est la solution la plus solide, mais pas si élégamment que le questionneur recherche, n'est-ce pas?
Kevin Simple
La seule chose que je changerais est d'ajouter le mot-clé static à la méthode. ;-)
Robert S.
Nécessite des indicateurs de limite, c'est-à-dire InRange (number, lowerBound, LOWER_IS_INCLUSIVE, Upperbound, UPPER_IS_EXCLUSIVE) pour permettre <vs <=. J'ai écrit ceci dans l'intention d'être sournois, mais maintenant que j'y pense, les drapeaux encourageraient en fait l'appelant à clarifier ses spécifications.
William T.Mallard
56

Juste pour ajouter au bruit ici, vous pouvez créer une méthode d'extension:

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

Ce qui vous permettrait de faire quelque chose comme ...

int val = 15;

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

Cela étant dit, cela semble être une chose idiote à faire lorsque le chèque lui-même n'est qu'une ligne.

Adam Robinson
la source
1
@Ben: Je suis allé sur le sujet, qui dit "dans une fourchette" (ce que je ne pense pas être ambigu à cet égard), mais vous avez raison en ce que le corps de la question dit "entre 1 et 100" (ce qui est , bien sûr, ambigu).
Adam Robinson
48

Comme d'autres l'ont dit, utilisez un simple if.

Vous devriez penser à la commande.

par exemple

1 <= x && x <= 100

est plus facile à lire que

x >= 1 && x <= 100
Esben Skov Pedersen
la source
19
«Plus facile» est dans l'œil du spectateur. Personnellement, je préfère avoir la variable en question à gauche et la constante ou variable non en question à droite.
Adam Robinson
15
En Perl 6 , vous écririez 1 <= x <= 100.
Jordão
2
L'ordre des lignes numériques est le plus clair au départ - mais vous pouvez entraîner vos yeux / votre esprit pour d'autres commandes. Plus précisément, j'aime le truc de placer la constante à gauche, toujours. Si vous faites cela, le compilateur vous dira quand vous avez tapé =au lieu de ==. Cela n'aide pas avec les opérateurs relationnels de non-égalité - mais il est facile de s'habituer à l'utiliser de manière cohérente.
davidbak
1
Je veux juste ajouter que cette solution n'est en aucun cas utile. Considérons xun appel de fonction complexe ou une expression Linq qui prend du temps. Dans ce cas, vous feriez cela deux fois, ce qui n'est pas une bonne chose. Bien sûr, vous devriez stocker la valeur dans une variable locale temporaire, mais il existe certains cas (par exemple dans les instructions else-if) où vous ne voulez appeler les fonctions qu'après l'échec des autres if ou else-if. Avec les variables temporaires, vous devez de toute façon les appeler avant. Une méthode d'extension (mentionnée dans d'autres réponses) est la meilleure solution à mon humble avis dans ces cas.
Robert S.
4
J'aime aussi l'ordre des droites numériques, et aussi pour le test du complément, par exemple x <10 || 20 <x. Pour moi, il crie "x est en dehors de la plage 10-20".
William T.Mallard
44

Dans le code de production, j'écrirais simplement

1 <= x && x <= 100

C'est facile à comprendre et très lisible.


Voici une méthode intelligente qui réduit le nombre de comparaisons de deux à un en utilisant des mathématiques. L'idée est que l'un des deux facteurs devient négatif si le nombre se situe en dehors de la plage et zéro si le nombre est égal à l'une des bornes:

Si les limites sont inclusives:

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

ou

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

Si les limites sont exclusives:

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

ou

(x - min) * (max - x) > 0
Olivier Jacot-Descombes
la source
3
Selon mes critères, c'est de loin la solution la plus élégante, intéressante est que pour moi, elle semble également fonctionner un peu plus vite que de vérifier les deux expressions, cela dit que cela semble également plus incohérent (la vitesse semble varier davantage) serait intéressant à voir s'il y a des recherches effectuées sur laquelle est la plus rapide.
Thomas Lindvall
3
Testé votre solution sur javascript et sa précision avec des nombres à virgule flottante jusqu'à 14 décimales. C'est un très bon extrait de code. Si je pouvais, ça vous voterait trois fois
rubbyrubber
4
Cependant, il y a un problème mineur si de grands nombres positifs sont impliqués, il peut déborder! XD Vous voudrez peut-être garder cela à l'esprit lors de l'écriture de votre code.
BrainStorm.exe
2
La question demande de l'élégance et est donc plus académique que pratique. Personnellement, je n'utiliserais qu'un simple 1 < x && x < 100code productif. C'est plus facile à comprendre.
Olivier Jacot-Descombes
1
Pour ceux qui sont préoccupés par les performances, 1 < x & x < 100(no && short circuit) indique au compilateur qu'il peut toujours évaluer x < 100quel que soit le résultat de 1 < x. Curieusement (en raison de la prédiction de branche), il est plus rapide de toujours faire cette opération simple que de l'ignorer parfois.
Tom Leys
23

Je propose ceci:

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

Exemples:

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

et bien sûr avec des variables:

myvalue.IsWithin(min, max)

Il est facile à lire (proche du langage humain) et fonctionne avec tout type comparable (entiers, doubles, types personnalisés ...).

Avoir un code facile à lire est important car le développeur ne perdra pas de «cycles cérébraux» pour le comprendre. Dans les longues sessions de codage, les cycles cérébraux gaspillés rendent le développeur fatigué plus tôt et sujet aux bogues.

Anton M
la source
3
je simplifierais encore plus en utilisant le mot entre et en ayant un drapeau booléen pour déterminer s'il est inclusif ou non
Ben
Bien. C'est facile à comprendre. J'ai changé le nom 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 fonctionnerait mais je n'aime pas les conditions négatives
Paulustrious
21

Avec un peu d'abus de méthode d'extension, nous pouvons obtenir la solution «élégante» suivante:

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!");
        }
    }
}
Ferruccio
la source
Aimez la solution! BTW pour soutenir inclusive, créer enum Inclusivedes valeurs: Lower, Upper, All. Et pour passer la Infonction d' un paramètre supplémentaire de type enum Inclusiveavec la valeur par défaut Inclusive.All, mettez à jour le Tocorps de la fonction poignée All, Lower, les Uppervaleurs :)
Nikita
7

Si c'est accidentel, un simple ifest tout ce dont vous avez besoin. Si cela se produit dans de nombreux endroits, vous voudrez peut-être considérer ces deux éléments:

  • PostSharp . Décorez les méthodes avec des attributs qui «injectent» du code dans la méthode après la compilation. Je ne sais pas avec certitude, mais je peux imaginer qu'il peut être utilisé pour cela.

Quelque chose comme:

[Between("parameter", 0, 100)]
public void Foo(int parameter)
{
}
  • Contrats de code . A l'avantage que les contraintes peuvent être vérifiées au moment de la compilation, par vérification statique de votre code et des endroits qui utilisent votre code.
JulianR
la source
+1 pour les contrats de code; c'est spécifique à la validation d'un paramètre, mais c'est un cas d'utilisation fréquent et la vérification statique a le potentiel d'être extrêmement utile.
Dan Bryant
5
if (value > 1 && value < 100)
{
    // do work
}
else
{
    // handle outside of range logic
}
Nick Larsen
la source
5

Utiliser une &&expression pour joindre deux comparaisons est simplement la manière la plus élégante de le faire. Si vous essayez d'utiliser des méthodes d'extension sophistiquées et autres, vous vous demandez s'il faut inclure la limite supérieure, la limite inférieure ou les deux. Une fois que vous commencez à ajouter des variables supplémentaires ou à modifier les noms d'extension pour indiquer ce qui est inclus, votre code devient plus long et plus difficile à lire (pour la grande majorité des programmeurs). De plus, des outils comme Resharper vous avertiront si votre comparaison n'a pas de sens ( number > 100 && number < 1), ce qu'ils ne feront pas si vous utilisez une méthode ('i.IsB Between (100, 1)').

Le seul autre commentaire que je ferais est que si vous vérifiez les entrées avec l'intention de lever une exception, vous devriez envisager d'utiliser des contrats de code:

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

C'est plus élégant que if(...) throw new Exception(...), et vous pouvez même obtenir des avertissements lors de la compilation si quelqu'un essaie d'appeler votre méthode sans vous assurer que le nombre est dans les limites en premier.

StriplingGuerrier
la source
2
Pour info, l'analyseur statique de contrats est plus heureux lorsque les contraintes de limite inférieure et supérieure sont divisées en instructions Requiert distinctes.
Dan Bryant
Merci Dan Bryant, c'est précisément ce que je cherchais ici. Impossible de trouver beaucoup de matériel sur les suggestions sur le style des conditions pour les méthodes Requiert et d'autres méthodes de contrat de code associées.
jpierson
2

Si vous voulez écrire plus de code qu'un simple if, vous pouvez peut-être: Créer une méthode d'extension appelée 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());

Addenda:il est intéressant de noter que dans la pratique, vous ne "vérifiez que l'égalité" (ou <,>) dans une base de code. (Sauf dans les situations les plus triviales.) À titre d'exemple, tout programmeur de jeu utiliserait des catégories quelque chose comme ce qui suit dans chaque projet, comme une question de base. Notez que dans cet exemple, il utilise (se trouve être) une fonction (Mathf.Approximately) qui est intégrée à cet environnement; dans la pratique, vous devez généralement développer soigneusement vos propres concepts sur ce que les comparaisons signifient pour les représentations informatiques de nombres réels, pour le type de situation que vous concevez. (Ne dites même pas que si vous faites quelque chose comme, peut-être un contrôleur, un contrôleur PID ou autre, tout le problème devient central et très difficile, cela devient la nature du projet.

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;
    }
Tony
la source
1
Remplacez if et else parreturn (value >= Min && value <= Max);
AeroX
la manière élégante d'écrire la comparaison est "dans l'ordre logique ..." if (Min <= valeur && valeur <= Max). C'est beaucoup plus joli.
Fattie
2
Plus loin sur cette question, il est tellement surprenant que personne n'ait mentionné le problème central dans aucun projet du monde réel (en particulier si vous êtes un ingénieur de jeu) est que vous devez gérer le problème d'approximation . Dans tout logiciel du monde réel, vous ne "faites qu'une comparaison" (qu'il s'agisse d'égalité ou <,>), vous devez considérer et gérer le problème d'erreur, en fonction de la situation. J'ai édité un addendum à cette réponse (la seule bonne réponse ici!) Car aucune autre réponse n'est autorisée.
Fattie
Merci pour cette observation et l'addendum.
Tony
2

Parce que toutes les autres réponses ne sont pas inventées par moi, voici juste ma mise en œuvre:

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

Vous pouvez ensuite l'utiliser comme ceci:

var value = 5;
var start = 1;
var end = 10;

var result = value.IsWithin(start, end, Range.Closed);
Oliver
la source
2

MODIFIER: Nouvelle réponse fournie. Je commençais à peine à utiliser C # lorsque j'ai écrit la première réponse à cette question, et avec le recul je me rends compte maintenant que ma «solution» était / est naïve et inefficace.

Ma réponse originale: j'irais avec la version la plus simple:

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

Une meilleure façon

Comme je n'ai vu aucune autre solution plus efficace (au moins d'après mes tests), je vais essayer de nouveau.

Nouvelle et meilleure méthode qui fonctionne également avec des plages négatives :

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

Cela peut être utilisé avec des plages positives et négatives et par défaut sur une plage de

1..100 (inclus) et utilise xcomme nombre à vérifier suivi d'une plage facultative définie par minet max.

Ajouter des exemples pour une bonne mesure

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

Retour:

True
True
True
False
True

Exemple 2: Utilisation d'une liste de nombres entiers aléatoires entre 1 et 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);

Retour:

66660 ints found in range 1..100

Temps d'exécution: 0.016 seconde (s)

cseder
la source
Oui, je commente un commentaire à ma réponse de 2013 :) @RyanTheLeach: En quoi ma réponse à cette question est-elle différente de la réponse désormais «acceptée»? Je me rends compte que ce n'est pas la traversée la plus efficace, mais «terrible»? À quel point l'allocation et la boucle sur 100 pouces peuvent-elles être mauvaises? En 1950, ce n'était probablement pas socialement accepté, mais ...
cseder le
@RyanTheLeach Je ne vous en veux pas ... J'ai mis à jour ma réponse, donc, si vous connaissez une solution encore plus efficace, veuillez élaborer!
cseder le
1
J'ai supprimé mes commentaires car ils ne tiennent plus. Merci pour le correctif, cela semble correct.
Ryan The Leach
1

Une nouvelle tournure sur un vieux favori:

public bool IsWithinRange(int number, int topOfRange, int bottomOfRange, bool includeBoundaries) {
    if (includeBoundaries)
        return number <= topOfRange && number >= bottomOfRange;
    return number < topOfRange && number > bottomOfRange;
}
Ben Hoffstein
la source
3
Il existe en fait quatre cas, inclusif / inclusif, inclusif / exclusif, exclusif / inclusif et exclusif / exclusif.
William T.Mallard
1

En C, si l'efficacité du temps est cruciale et que les débordements d'entiers seront bouclés, on pourrait le faire if ((unsigned)(value-min) <= (max-min)) .... Si 'max' et 'min' sont des variables indépendantes, la soustraction supplémentaire pour (max-min) perdra du temps, mais si cette expression peut être précalculée au moment de la compilation, ou si elle peut être calculée une fois au moment de l'exécution pour en tester plusieurs nombres par rapport à la même plage, l'expression ci-dessus peut être calculée efficacement même dans le cas où la valeur est dans la plage (si une grande fraction de valeurs sera inférieure à la plage valide, il peut être plus rapide à utiliserif ((value >= min) && (value <= max)) ... car elle se terminera tôt si value est inférieure à min).

Avant d'utiliser une telle implémentation, cependant, comparez la machine cible d'une personne. Sur certains processeurs, l'expression en deux parties peut être plus rapide dans tous les cas puisque les deux comparaisons peuvent être effectuées indépendamment alors que dans la méthode de soustraction et de comparaison, la soustraction doit être terminée avant que la comparaison puisse s'exécuter.

supercat
la source
1

Que diriez-vous quelque chose comme ça?

if (theNumber.isBetween(low, high, IntEx.Bounds.INCLUSIVE_INCLUSIVE))
{
}

avec la méthode d'extension comme suit (testé):

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;
    }
}
William T. Mallard
la source
1

Je ferais un objet Range, quelque chose comme ceci:

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

Ensuite, vous l'utilisez de cette façon:

Range<int> myRange = new Range<int>(1,999);
bool isWithinRange = myRange.IsWithinBoundaries(3);

De cette façon, vous pouvez le réutiliser pour un autre type.

IEatBagels
la source
Votre Rangeobjet doit utiliser la CompareTométhode pour comparer les éléments, pas l' <opérateur.
Servy le
Vous avez raison, mais si vous implémentez IComparable, vous devez également remplacer les opérateurs (du moins c'est ce que dit mon analyse de code VS), ce qui signifie que <fonctionnerait. Bien que je puisse me tromper, je n'ai pas beaucoup d'expérience et c'est ma première réponse sur SO
IEatBagels
Non, votre compilateur ne dira pas que cela fonctionne. Cela ne compilera pas. Il est tout à fait raisonnable qu'un objet implémente IComparableet ne surcharge pas l' <opérateur.
Servy le
1

Lorsque vous vérifiez si un «nombre» est dans une plage, vous devez être clair dans ce que vous voulez dire, et que signifie deux nombres égaux? En général, vous devriez envelopper tous les nombres à virgule flottante dans ce qu'on appelle une «boule epsilon». Ceci est fait en choisissant une petite valeur et en disant que si deux valeurs sont si proches, elles sont la même chose.

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

Avec ces deux aides en place et en supposant que si un nombre quelconque peut être moulé en double sans la précision requise. Tout ce dont vous aurez besoin maintenant est une énumération et une autre méthode

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

L'autre méthode suit:

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

Maintenant, c'est peut-être bien plus que ce que vous vouliez, mais cela vous empêche de traiter d'arrondi tout le temps et d'essayer de vous rappeler si une valeur a été arrondie et à quel endroit. Si vous en avez besoin, vous pouvez facilement l'étendre pour fonctionner avec n'importe quel epsilon et permettre à votre epsilon de changer.

rahicks
la source
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);
    }
}

Usage

double numberToBeChecked = 7;

var result = numberToBeChecked.IsB Between (100.122);

var result = 5.IsB Between (100,120);

résultat var = 8,0.IsB Between (1,2,9,6);

İBRAHİM GAZALOĞLU
la source
1

Si vous êtes préoccupé par le commentaire de @Daap sur la réponse acceptée et que vous ne pouvez transmettre la valeur qu'une seule fois, vous pouvez essayer l'une des solutions suivantes

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

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

ou

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

//var t = TestRangeMargin(10, somelist.Count(), 5);
Hugo Delsing
la source
1

Concernant l'élégance, le plus proche de la notation mathématique ( a <= x <= b ) améliore légèrement la lisibilité:

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

Pour plus d'illustration:

public static bool IsOutside(this int value, int min, int max)
{
    return value < min || max < value;
}
hector-j-rivas
la source
0

Je cherchais une manière élégante de le faire où les limites pourraient être commutées (c'est-à-dire pas sûr de l'ordre dans lequel les valeurs sont).

Cela ne fonctionnera que sur les versions plus récentes de C # où le?: Existe

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

Évidemment, vous pouvez modifier les signes = pour vos besoins. Pourrait aussi avoir de la fantaisie avec le casting de type. J'avais juste besoin d'un retour flottant dans les limites (ou égal à)

Kalikovision
la source
0

Élégant car il ne vous oblige pas à déterminer laquelle des deux valeurs limites est la plus élevée en premier. Il ne contient pas non plus de branches.

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 );
}
Tom Leys
la source
& + | sont des opérateurs
binaires
0

Je ne sais pas mais j'utilise cette méthode:

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

Et voici comment je peux l'utiliser:

    [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
la source
Veuillez fournir un exemple d'utilisation sous le bloc de code, cela aidera OP à savoir si cela correspond à son objectif
Gabriel Balsa Cantú
0

Voici quelques méthodes d'extension qui peuvent aider

  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;
    }
Hanan
la source
0

Si c'est pour valider les paramètres de méthode, aucune des solutions ne lève ArgumentOutOfRangeException et permet une configuration facile / correcte des valeurs min / max inclusives / exclusives.

Utilisez comme ça

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

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

Je viens d'écrire ces belles fonctions. Il a également l'avantage de ne pas avoir de branchement (un seul if) pour les valeurs valides. Le plus difficile est de créer les bons messages d'exception.

/// <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);
Etienne Charland
la source
-2

Vous cherchez in [1..100]? Ce n'est que Pascal.

Polluks
la source
2
Pas vrai, ce n'est pas seulement Pascal. De nombreuses langues modernes ont des fonctionnalités comme celle-ci. Dans Kotlin, par exemple, cela s'appelle "Pattern Matching". Exemple when (number) { in 0..9 -> println("1 digit") in 10..99 -> println("2 digits") in 100..999 -> println("3 digits") }
this.myself