¿Cómo verificar con elegancia si un número está dentro de un rango?

157

¿Cómo puedo hacer esto con elegancia con C # y .NET 3.5 / 4?

Por ejemplo, un número puede estar entre 1 y 100.

Sé que un simple si sería suficiente; pero la palabra clave para esta pregunta es elegancia. Es para mi proyecto de juguete, no para producción.

Estas preguntas no se referían a la velocidad, sino a la belleza del código. Deja de hablar de eficiencia y tal; recuerda que estás predicando al coro.

Sergio Tapia
fuente
23
Re: Su "edición" - simple es elegante . Personalmente, considero que la declaración if es más elegante que cualquier medio no estándar de hacer esta verificación ...
Reed Copsey
44
"Todo debe hacerse lo más simple posible, pero no más simple". - Albert Einstein
corsiKa
3
@ Sergio: No siento que estoy siendo pedante. Siento que las personas a menudo abusan de los métodos de extensión y otras herramientas en el lenguaje para reemplazar cosas que ya son simples. Hay cientos de formas de comparar dos valores int, pero usar cualquier cosa menos la más obvia es una mala elección, IMO.
Reed Copsey
3
@ Sergio: supongo, entonces, no veo el punto de la pregunta;)
Reed Copsey
66
@ Sergio: si ifno es "barroco", no lo arregles.
StriplingWarrior

Respuestas:

154

Hay muchas opciones:

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

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

Además, consulte esta publicación SO para ver las opciones de expresiones regulares.

Dustin Laine
fuente
334
Enumerable.Range tiene que generar primero el número enumerable de enteros y luego recorrer cada elemento para encontrarlo. Esa es una idea terrible y el rendimiento en comparación con la comprobación de un valor es drásticamente diferente. Creo que deberíamos adoptar una moto, solo porque las Extensiones LINQ son geniales, no significa que deberían usarse para todo.
Matthew Abbott
14
@Matthew: stackoverflow.com/questions/777400/…
Adam Robinson el
15
Estoy de acuerdo en que esta es una idea terrible en cuanto al rendimiento, pero el OP quiere algo más elegante que una ifdeclaración. Esto ciertamente logra que ...;)
Tim Coker
10
Vale la pena señalar que el segundo parámetro no es "detener", sino "contar". Entonces, por ejemplo, Enumerable.Range (150, 300) .Contains (400) devolverá verdadero.
Shathur 01 de
55
Por favor no use esta respuesta . Tendrá un rendimiento horrible si sus rangos son bastante grandes. Por favor, vea la respuesta de @ olivier-jacot-descombes
Aaron Hudon
95

¿Quieres decir?

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

o

bool TestRange (int numberToCheck, int bottom, int top)
{
  return (numberToCheck >= bottom && numberToCheck <= top);
}
kemiller2002
fuente
1
No necesita "es" allí ... Esto no se compilará. (De lo contrario, estoy de acuerdo al 100%)
Reed Copsey
44
@Ben, solo espera hasta que intente patentarlo también :)
kemiller2002
Creo que esta es la solución más sólida, pero no tan elegante como el que pregunta, ¿no?
Kevin Simple
Lo único que cambiaría es agregar la palabra clave estática al método. ;-)
Robert S.
Necesita indicadores de límite, es decir, InRange (número, lowerBound, LOWER_IS_INCLUSIVE, Upperbound, UPPER_IS_EXCLUSIVE) para permitir <vs <=. Escribí esto con la intención de ser sarcástico, pero ahora que lo pienso, las banderas realmente alentarían a la persona que llama a aclarar sus especificaciones.
William T. Mallard
56

Solo para agregar al ruido aquí, podría crear un método de extensión:

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

Lo que te permitiría hacer algo como ...

int val = 15;

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

Dicho esto, esto parece una tontería cuando el cheque en sí es solo una línea.

Adam Robinson
fuente
1
@Ben: Fui al tema, que dice "dentro de un rango" (que no creo que sea ambiguo en ese sentido), pero tiene razón en que el cuerpo de la pregunta dice "entre 1 y 100" (que es , por supuesto, ambiguo).
Adam Robinson el
48

Como otros dijeron, use un simple if.

Debes pensar en el pedido.

p.ej

1 <= x && x <= 100

es más fácil de leer que

x >= 1 && x <= 100
Esben Skov Pedersen
fuente
19
"Más fácil" está en el ojo del espectador. Personalmente prefiero tener la variable en cuestión a la izquierda y la constante o variable no en cuestión a la derecha.
Adam Robinson el
15
En Perl 6 , escribirías 1 <= x <= 100.
Jordão
2
El orden de la línea numérica es el más claro inicialmente, pero puede entrenar sus ojos / mente para otros pedidos. Específicamente, me gusta el truco de colocar la constante a la izquierda, siempre. Si haces eso, el compilador te dirá cuándo has escrito en =lugar de ==. No ayuda con los operadores relacionales que no son de igualdad, pero es fácil acostumbrarse a usarlo de manera consistente.
davidbak
1
Solo quiero agregar que esta solución no es útil en ningún caso. Considerar xes una llamada de función compleja o una expresión de Linq que consume mucho tiempo. En este caso, harías esto dos veces, lo que no es bueno. Claro que debe almacenar el valor en una variable local temporal, pero hay algunos casos (por ejemplo, en sentencias else-if) en los que solo desea llamar a las funciones después de que otros if's o else-if hayan fallado. Con las variables temporales, debe llamarlas de todos modos antes. Un método de extensión (mencionado en otras respuestas) es la mejor solución en mi caso.
Robert S.
44
También me gusta el orden de las líneas numéricas, y también para la prueba del complemento, por ejemplo, x <10 || 20 <x. Para mí, grita "x está fuera del rango 10-20".
William T. Mallard
44

En el código de producción simplemente escribiría

1 <= x && x <= 100

Esto es fácil de entender y muy legible.


Aquí hay un método inteligente que reduce el número de comparaciones de dos a uno mediante el uso de algunas matemáticas. La idea es que uno de los dos factores se vuelve negativo si el número se encuentra fuera del rango y cero si el número es igual a uno de los límites:

Si los límites son inclusivos:

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

o

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

Si los límites son exclusivos:

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

o

(x - min) * (max - x) > 0
Olivier Jacot-Descombes
fuente
3
Según mis estándares, esta es, con mucho, la solución más elegante, lo interesante es que para mí también parece correr un poco más rápido que verificar las dos expresiones, eso dijo que también parece más inconsistente (la velocidad parece variar más) sería interesante ver si hay alguna investigación realizada sobre cuál es la más rápida.
Thomas Lindvall
3
Probó su solución en javascript y es precisa con números de coma flotante de hasta 14 decimales. Es un muy buen fragmento de código. Te votaría tres veces si pudiera
rubbyrubber
44
Sin embargo, hay un problema menor si se trata de grandes números positivos, ¡puede desbordarse! XD Es posible que desee tener eso en cuenta al escribir su código.
BrainStorm.exe
2
La pregunta pide elegancia y, por lo tanto, tiene más valor académico que práctico. Personalmente, usaría un 1 < x && x < 100código simple y productivo. Es más fácil de entender.
Olivier Jacot-Descombes
1
Para aquellos preocupados por el rendimiento, 1 < x & x < 100(no && short circuit) le indica al compilador que siempre puede evaluar x < 100sin importar el resultado 1 < x. Curiosamente (debido a la predicción de ramificación) es más rápido hacer siempre esta operación simple que a veces omitirla.
Tom Leys
23

Propongo esto:

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

Ejemplos:

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

y por supuesto con variables:

myvalue.IsWithin(min, max)

Es fácil de leer (cercano al lenguaje humano) y funciona con cualquier tipo comparable (entero, doble, tipos personalizados ...).

Tener un código fácil de leer es importante porque el desarrollador no desperdiciará "ciclos cerebrales" para comprenderlo. En largas sesiones de codificación, los ciclos cerebrales desperdiciados hacen que el desarrollador se canse antes y sea propenso a errores.

Anton M
fuente
3
simplificaría aún más usando la palabra en medio, y tener una bandera booleana para determinar si es inclusivo o no
Ben
Bueno. Es facil de entender. Cambié el nombre 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 funcionaría pero no me gustan las condiciones negativas
Paulustrious
21

Con un poco de abuso del método de extensión, podemos obtener la siguiente solución "elegante":

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
fuente
¡Me gusta la solución! Por cierto para apoyar inclusive, crear enum Inclusivecon los valores: Lower, Upper, All. Y pasar a la Infunción de un parámetro adicional de tipo enum Inclusivecon el valor predeterminado Inclusive.All, actualizar el Tocuerpo de la función de asa All, Lower, Uppervalores :)
Nikita
7

Si esto es incidental, un simple ifes todo lo que necesita. Si esto sucede en muchos lugares, es posible que desee considerar estos dos:

  • PostSharp . Decora métodos con atributos que 'inyectan' código en el método después de la compilación. No estoy seguro, pero me imagino que se puede usar para esto.

Algo como:

[Between("parameter", 0, 100)]
public void Foo(int parameter)
{
}
  • Código de contratos . Tiene la ventaja de que las restricciones se pueden verificar en tiempo de compilación, mediante la verificación estática de su código y los lugares que usan su código.
JulianR
fuente
+1 para contratos de código; es específico para validar un parámetro, pero es un caso de uso frecuente y la verificación estática tiene el potencial de ser extremadamente útil.
Dan Bryant
5
if (value > 1 && value < 100)
{
    // do work
}
else
{
    // handle outside of range logic
}
Nick Larsen
fuente
5

Usar una &&expresión para unir dos comparaciones es simplemente la forma más elegante de hacerlo. Si intenta utilizar métodos de extensión sofisticados y demás, se encontrará con la pregunta de si incluir el límite superior, el límite inferior o ambos. Una vez que comience a agregar variables adicionales o cambie los nombres de las extensiones para indicar qué está incluido, su código se vuelve más largo y difícil de leer (para la gran mayoría de los programadores). Además, herramientas como Resharper te avisarán si tu comparación no tiene sentido (number > 100 && number < 1 ), lo que no harán si utiliza un método ('i.IsBetween (100, 1)').

El único otro comentario que haría es que si está verificando entradas con la intención de lanzar una excepción, debería considerar usar contratos de código:

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

Esto es más elegante que if(...) throw new Exception(...), e incluso podría obtener advertencias en tiempo de compilación si alguien intenta llamar a su método sin asegurarse de que el número esté dentro de los límites primero.

StriplingWarrior
fuente
2
Para su información, el analizador estático de contratos es más feliz cuando las restricciones de límite inferior y superior se dividen en declaraciones de Requiere separadas.
Dan Bryant el
Gracias Dan Bryant, eso es precisamente lo que estaba buscando aquí. No se puede encontrar mucho material sobre sugerencias sobre el estilo de condiciones para el Requiere y otros métodos de Contrato de Código relacionados.
jpierson
2

Si desea escribir más código que un simple if, tal vez pueda: Crear un Método de extensión llamado 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());

Apéndice:Vale la pena señalar que en la práctica rara vez "simplemente verifica la igualdad" (o <,>) en una base de código. (Aparte de en las situaciones más triviales). Simplemente como ejemplo, cualquier programador de juegos usaría categorías como las siguientes en cada proyecto, como una cuestión básica. Tenga en cuenta que en este ejemplo (resulta ser) que usa una función (Mathf. Aproximadamente) que está integrada en ese entorno; En la práctica, normalmente tiene que desarrollar cuidadosamente sus propios conceptos de lo que significan las comparaciones para las representaciones de números reales por computadora, para el tipo de situación que está diseñando. (Ni siquiera mencione que si está haciendo algo como, tal vez un controlador, un controlador PID o similar, todo el problema se vuelve central y muy difícil, se convierte en la naturaleza del proyecto.

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
fuente
1
Reemplace el if y else conreturn (value >= Min && value <= Max);
AeroX
la forma elegante de escribir la comparación es "en orden lógico ..." if (Min <= value && value <= Max). Eso es mucho más bonito.
Fattie
2
Más adelante en esta pregunta, es tan sorprendente que nadie haya mencionado el problema central en ningún proyecto del mundo real (particularmente si eres un ingeniero de juegos) es que tienes que lidiar con el problema de la aproximación . En cualquier software del mundo real, esencialmente nunca "solo hace una comparación" (ya sea igualdad o <,>) debe considerar y tratar el problema del error, dependiendo de la situación en cuestión. He editado en un anexo a esta respuesta (¡la única respuesta correcta aquí!) Ya que no se permiten más respuestas.
Fattie
Gracias por esta observación y el apéndice.
Tony
2

Porque todas las otras respuestas no las inventé yo, aquí solo mi implementación:

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

Luego puede usarlo así:

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

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

EDITAR: Nueva respuesta proporcionada. Estaba comenzando a usar C # cuando escribí la primera respuesta a esta pregunta, y en retrospectiva ahora me doy cuenta de que mi "solución" era / es ingenua e ineficiente.

Mi respuesta original: iría con la versión más simple:

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

Una mejor manera

Como no he visto ninguna otra solución que sea más eficiente (al menos según mis pruebas), lo intentaré de nuevo.

Nueva y mejor manera que también funciona con rangos negativos :

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

Esto se puede usar con rangos positivos y negativos y los valores predeterminados para un rango de

1..100 (inclusive) y se utiliza xcomo número para verificar seguido de un rango opcional definido por miny max.

Agregar ejemplos de buena medida

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

Devoluciones:

True
True
True
False
True

Ejemplo 2: uso de una lista de entradas aleatorias entre 1 y 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);

Devoluciones:

66660 ints found in range 1..100

Tiempo de ejecución: 0.016 segundo (s)

cseder
fuente
Sí, estoy comentando un comentario a mi respuesta de 2013 :) @RyanTheLeach: ¿Cómo es mi respuesta a esta pregunta diferente de la respuesta ahora "aceptada"? Me doy cuenta de que no es el recorrido más efectivo, sino "terrible". ¿Qué tan malo puede ser asignar y recorrer 100 ints? En 1950 probablemente no fue aceptado socialmente, pero ...
Cseder
@RyanTheLeach No te culpo ... He actualizado mi respuesta, así que, si conoces una solución que sea aún más eficiente, ¡por favor elabora!
cseder
1
He eliminado mis comentarios porque ya no están en pie. Gracias por la solución, parece estar bien.
Ryan The Leach
1

Un nuevo giro en un viejo favorito:

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
fuente
3
En realidad, hay cuatro casos, inclusivo / inclusivo, inclusivo / exclusivo, exclusivo / inclusivo y exclusivo / exclusivo.
William T. Mallard
1

En C, si la eficiencia del tiempo es crucial y se desbordan los desbordamientos de enteros, se podría hacer if ((unsigned)(value-min) <= (max-min)) .... Si 'max' y 'min' son variables independientes, la resta adicional para (max-min) perderá tiempo, pero si esa expresión puede calcularse previamente en tiempo de compilación, o si puede calcularse una vez en tiempo de ejecución para probar muchas números contra el mismo rango, la expresión anterior se puede calcular de manera eficiente incluso en el caso de que el valor esté dentro del rango (si una gran fracción de valores estará por debajo del rango válido, puede ser más rápido de usar if ((value >= min) && (value <= max)) ...porque saldrá temprano si el valor es menos de min).

Sin embargo, antes de usar una implementación como esa, evalúe la máquina de destino de uno. En algunos procesadores, la expresión de dos partes puede ser más rápida en todos los casos ya que las dos comparaciones se pueden hacer de forma independiente, mientras que en el método de restar y comparar la resta tiene que completarse antes de que la comparación pueda ejecutarse.

Super gato
fuente
1

¿Qué tal algo como esto?

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

con el método de extensión de la siguiente manera (probado):

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
fuente
1

Haría un objeto Range, algo como esto:

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

Luego lo usas de esta manera:

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

De esa manera puedes reutilizarlo para otro tipo.

IEatBagels
fuente
Su Rangeobjeto necesita usar el CompareTométodo para comparar elementos, no el <operador.
Servy
Tiene razón, aunque si implementa IComparable también debería anular los operadores (al menos eso es lo que dice mi análisis de código VS), lo que significa que <funcionaría. Aunque podría estar equivocado, no tengo mucha experiencia y esta es mi primera respuesta en SO
IEatBagels
No, tu compilador no dirá que esto funciona. Esto no se compilará. Es completamente razonable que un objeto implemente IComparabley no sobrecargue al <operador.
Servy
1

Al verificar si un "Número" está en un rango, debe ser claro en lo que quiere decir, y ¿qué significan dos números iguales? En general, debe envolver todos los números de coma flotante en lo que se llama una 'bola épsilon'. Esto se hace seleccionando un valor pequeño y diciendo que si dos valores están tan cerca, son lo mismo.

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

Con estos dos ayudantes en su lugar y suponiendo que si algún número se puede lanzar como doble sin la precisión requerida. Todo lo que necesitará ahora es una enumeración y otro método

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

El otro método sigue:

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

Ahora, esto puede ser mucho más de lo que quería, pero le impide tratar de redondear todo el tiempo y tratar de recordar si un valor se ha redondeado y a qué lugar. Si lo necesita, puede extenderlo fácilmente para trabajar con cualquier épsilon y permitir que su épsilon cambie.

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

Uso

double numberToBeChecked = 7;

resultado var = numberToBeChecked.IsBetween (100,122);

resultado var = 5.IsBetween (100,120);

resultado var = 8.0.IsBetween (1.2,9.6);

İBRAHİM GAZALOĞLU
fuente
1

Si le preocupa el comentario de @Daap sobre la respuesta aceptada y solo puede pasar el valor una vez, puede intentar uno de los siguientes

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

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

o

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

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

En cuanto a la elegancia, lo más parecido a la notación matemática ( a <= x <= b ) mejora ligeramente la legibilidad:

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

Para más ilustración:

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

Estaba buscando una manera elegante de hacerlo donde los límites podrían cambiarse (es decir, no estoy seguro en qué orden están los valores).

Esto solo funcionará en versiones más nuevas de C # donde el?: Existe

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

Obviamente, podría cambiar los signos = allí para sus propósitos. Podría ser elegante con el tipo de casting también. Solo necesitaba un retorno flotante dentro de los límites (o igual a)

Kalikovision
fuente
0

Elegante porque no requiere que determine cuál de los dos valores límite es mayor primero. Tampoco contiene ramas.

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
fuente
& + | son operadores bit a bit
nelsontruran
0

No lo sé pero uso este método:

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

Y esta es la forma en que puedo usarlo:

    [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);
    }
usuario8790965
fuente
Proporcione un ejemplo de uso debajo del bloque de código, esto ayudará a OP a saber si se ajusta a su propósito
Gabriel Balsa Cantú
0

Estos son algunos métodos de extensión que pueden ayudar

  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
fuente
0

Si se trata de validar los parámetros del método, ninguna de las soluciones arroja ArgumentOutOfRangeException y permite una configuración fácil / adecuada de valores min / max inclusivos / exclusivos.

Usar así

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

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

Acabo de escribir estas hermosas funciones. También tiene la ventaja de no tener ramificaciones (un solo if) para valores válidos. La parte más difícil es crear los mensajes de excepción adecuados.

/// <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
fuente
-2

Que estas buscando in [1..100]? Eso es solo Pascal.

Polluks
fuente
2
No es cierto, no es solo Pascal. Muchos idiomas modernos tienen características como esta. En Kotlin, por ejemplo, se llama "Coincidencia de patrones". Ejemplo when (number) { in 0..9 -> println("1 digit") in 10..99 -> println("2 digits") in 100..999 -> println("3 digits") }
this.myself