Como verificar elegantemente se um número está dentro de um intervalo?

157

Como posso fazer isso de maneira elegante com C # e .NET 3.5 / 4?

Por exemplo, um número pode estar entre 1 e 100.

Eu sei que um simples se seria suficiente; mas a palavra-chave para esta pergunta é elegância. É para o meu projeto de brinquedo, não para produção.

Essas perguntas não eram sobre velocidade, mas sobre a beleza do código. Pare de falar sobre eficiência e coisas do gênero; lembre-se de que você está pregando para o coral.

Sergio Tapia
fonte
23
Re: Seu "editar" - simples é elegante . Eu pessoalmente acho a instrução if mais elegante do que quaisquer meios não-padrão de fazer esta verificação ...
Reed Copsey
4
"Tudo deve ser feito o mais simples possível, mas não mais simples." - Albert Einstein
corsiKa
3
@Sergio: Eu não sinto que estou sendo pedante. Eu sinto que as pessoas frequentemente abusam de métodos de extensão e outras ferramentas no idioma para substituir coisas que já são simples. Existem centenas de maneiras de comparar dois valores int, mas usar qualquer coisa, menos o mais óbvio, é uma má escolha, IMO.
Reed Copsey
3
@Sergio: Eu acho que, então, eu não vejo o ponto da questão;)
Reed Copsey
6
@Sergio: se ifnão for "barroco", não conserte.
StriplingWarrior

Respostas:

152

Há muitas opções:

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

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

Além disso, confira esta postagem do SO para opções de regex.

Dustin Laine
fonte
334
Enumerable.Range deve gerar o enumerável de números inteiros primeiro e, em seguida, percorrer cada item para encontrá-lo. Essa é uma péssima ideia e o desempenho, comparado à verificação de um valor, é drasticamente diferente. Acho que devemos adotar uma moto, só porque as extensões LINQ são legais, não significa que elas devam ser usadas para tudo.
Matthew Abbott
14
@Matthew: stackoverflow.com/questions/777400/…
Adam Robinson
15
Concordo que essa é uma ideia terrível em termos de desempenho, mas o OP quer algo mais sofisticado do que uma ifdeclaração. Isso certamente realiza isso ...;)
Tim Coker
10
Vale ressaltar que o segundo parâmetro não é "parar", mas "contar". Por exemplo, Enumerable.Range (150, 300) .Contains (400) retornará true.
shathûr
5
Por favor, não use esta resposta . Ele terá um desempenho horrendo se seus intervalos forem bastante grandes. Por favor, veja a resposta por @ olivier-jacot-descombes
Aaron Hudon
95

Você quer dizer?

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

ou

bool TestRange (int numberToCheck, int bottom, int top)
{
  return (numberToCheck >= bottom && numberToCheck <= top);
}
kemiller2002
fonte
1
Você não precisa "está" lá ... Isso não será compilado. (Caso contrário, eu concordo 100%)
Reed Copsey
4
@ Ben, é só esperar até que eu tentar patenteá-lo também :)
kemiller2002
Eu acho que essa é a solução mais sólida, mas não é tão elegante quanto o pesquisador, não é?
Kevin Simple
A única coisa que eu mudaria é adicionar a palavra-chave estática ao método. ;-)
Robert S.
Precisa de sinalizadores de limite, ou seja, InRange (número, Limite inferior, LOWER_IS_INCLUSIVE, Upperbound, UPPER_IS_EXCLUSIVE) para permitir <vs <=. Eu escrevi isso com a intenção de ser irritante, mas agora que penso nisso, as bandeiras encorajariam o chamador a obter suas especificações corretamente.
William T. Mallard
56

Apenas para aumentar o ruído aqui, você pode criar um método de extensão:

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

O que permitiria fazer algo como ...

int val = 15;

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

Dito isto, isso parece uma coisa boba de se fazer quando o cheque em si é apenas uma linha.

Adam Robinson
fonte
1
@ Ben: Eu falei sobre o assunto, que diz "dentro de um intervalo" (o que eu não acho ambíguo a esse respeito), mas você está certo em que o corpo da pergunta diz "entre 1 e 100" (que é , é claro, ambíguo).
Adam Robinson
48

Como outros disseram, use um simples if.

Você deve pensar na encomenda.

por exemplo

1 <= x && x <= 100

é mais fácil de ler do que

x >= 1 && x <= 100
Esben Skov Pedersen
fonte
19
"Mais fácil" está nos olhos de quem vê. Eu pessoalmente prefiro ter a variável em questão à esquerda e à constante ou variável não em questão à direita.
Adam Robinson
15
No Perl 6 , você escreveria 1 <= x <= 100.
Jordão
2
O pedido da linha numérica é o mais claro inicialmente - mas você pode treinar seus olhos / mentes para outros pedidos. Especificamente - eu sempre gosto do truque de colocar a constante à esquerda. Se você fizer isso, o compilador informará quando você digitou em =vez de ==. Isso não ajuda com operadores relacionais sem igualdade - mas é fácil se acostumar a usá-lo de forma consistente.
Davidbak 22/03/16
1
Eu só quero acrescentar que esta solução não é útil em nenhum caso. Considerar xé uma chamada de função complexa ou uma expressão Linq demorada. Nesse caso, você faria isso duas vezes, o que não é uma coisa boa. Claro que você deve armazenar o valor em uma variável local temporária, mas há alguns casos (por exemplo, em instruções else-if-) em que você deseja chamar as funções somente após outras if's ou else-ifs falharam. Com variáveis ​​temporárias, você precisa chamá-las de qualquer maneira antes. Um método de extensão (mencionado em outras respostas) é a melhor solução imho nesses casos.
Robert S.
4
Também gosto da ordem das linhas numéricas e também para o teste do complemento, por exemplo, x <10 || 20 <x. Para mim, grita "x está fora do intervalo 10 - 20".
William T. Mallard
44

No código de produção, eu simplesmente escreveria

1 <= x && x <= 100

Isso é fácil de entender e muito legível.


Aqui está um método inteligente que reduz o número de comparações de dois para um usando um pouco de matemática. A ideia é que um dos dois fatores se torne negativo se o número estiver fora do intervalo e zero se o número for igual a um dos limites:

Se os limites forem inclusivos:

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

ou

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

Se os limites são exclusivos:

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

ou

(x - min) * (max - x) > 0
Olivier Jacot-Descombes
fonte
3
Pelos meus padrões, essa é de longe a solução mais elegante, interessante é que, para mim, também parece correr um pouco mais rápido do que verificar as duas expressões, dizendo que também parece mais inconsistente (a velocidade parece variar mais) seria interessante ver se houver alguma pesquisa feita sobre qual é a mais rápida.
22415 Thomas Thomasvvall
3
Teste sua solução em javascript e sua precisão com números de ponto flutuante até 14 casas decimais. É um snippet de código muito bom. Seria upvote você três vezes se eu pudesse
rubbyrubber
4
No entanto, existe um pequeno problema se houver um grande número positivo, ele pode transbordar! XD Você pode manter isso em mente ao escrever seu código.
BrainStorm.exe
2
A questão pede elegância e, portanto, tem mais valor acadêmico do que prático. Pessoalmente, eu usaria apenas um 1 < x && x < 100código produtivo simples . É mais fácil de entender.
Olivier Jacot-Descombes
1
Para aqueles preocupados com desempenho, 1 < x & x < 100(sem && curto-circuito) instrui o compilador que ele sempre pode avaliar, x < 100independentemente do resultado 1 < x. Estranhamente (devido à previsão de ramificação), é mais rápido sempre fazer essa operação simples do que às vezes ignorá-la.
Tom Leys
23

Eu proponho isso:

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

Exemplos:

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

e, claro, com variáveis:

myvalue.IsWithin(min, max)

É fácil de ler (próximo à linguagem humana) e funciona com qualquer tipo comparável (inteiro, duplo, tipos personalizados ...).

Ter código de fácil leitura é importante porque o desenvolvedor não desperdiçará "ciclos cerebrais" para entendê-lo. Em longas sessões de codificação, os ciclos cerebrais desperdiçados tornam o desenvolvedor cansado antes e propenso a erros.

Anton M
fonte
3
Gostaria de simplificar ainda mais, usando a palavra entre eles, e ter um sinalizador booleano para determinar se inclusiva ou não
Ben
Boa. É fácil de entender. Mudei o nome 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 iria funcionar, mas eu não gosto condições negativas
Paulustrious
21

Com um pouco de abuso do método de extensão, podemos obter a seguinte solução "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
fonte
Como a solução! Btw para apoiar inclusive, criar enum Inclusivecom os valores: Lower, Upper, All. E passar para a Infunção de um parâmetro adicional do tipo enum Inclusivecom valor padrão Inclusive.All, atualizar o Tocorpo da função de manusear All, Lower, Uppervalores :)
Nikita
7

Se isso for acidental, ifbasta um simples . Se isso acontecer em muitos lugares, você pode considerar estes dois:

  • PostSharp . Decore métodos com atributos que 'injetam' código no método após a compilação. Não sei ao certo, mas posso imaginar que possa ser usado para isso.

Algo como:

[Between("parameter", 0, 100)]
public void Foo(int parameter)
{
}
  • Código de contratos . Tem a vantagem de que as restrições podem ser verificadas em tempo de compilação, pela verificação estática do seu código e dos locais que o utilizam.
JulianR
fonte
+1 para contratos de código; é específico para validar um parâmetro, mas é um caso de uso frequente e a verificação estática pode ser extremamente útil.
Dan Bryant
5
if (value > 1 && value < 100)
{
    // do work
}
else
{
    // handle outside of range logic
}
Nick Larsen
fonte
5

Usar uma &&expressão para juntar duas comparações é simplesmente a maneira mais elegante de fazer isso. Se você tentar usar métodos de extensão sofisticados e coisas desse tipo, terá a questão de incluir o limite superior, o limite inferior ou ambos. Depois de começar a adicionar variáveis ​​adicionais ou alterar os nomes das extensões para indicar o que está incluído, seu código se torna mais longo e difícil de ler (para a grande maioria dos programadores). Além disso, ferramentas como o Resharper o alertarão se sua comparação não fizer sentido (number > 100 && number < 1 ), o que não acontecerá se você usar um método ('i.IsBetween (100, 1)').

O único outro comentário que eu faria é que, se você estiver verificando entradas com a intenção de lançar uma exceção, considere usar contratos de código:

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

Isso é mais elegante do que if(...) throw new Exception(...)e você pode receber avisos em tempo de compilação se alguém tentar chamar seu método sem garantir que o número esteja dentro dos limites primeiro.

StriplingWarrior
fonte
2
Para sua informação, o analisador estático dos contratos fica mais satisfeito quando as restrições do limite inferior e do limite superior são divididas em instruções Requer separadas.
Dan Bryant
Obrigado Dan Bryant, é exatamente isso que eu estava procurando aqui. Não é possível encontrar muito material sobre sugestões de estilo de condições para os métodos Requer e outros contratos de código relacionados.
jpierson
2

Se você quiser escrever mais código do que um simples se, talvez você possa: Crie um método de extensão chamado 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());

Termo aditivo:vale a pena notar que, na prática, você raramente "apenas verifica a igualdade" (ou <,>) em uma base de código. (Exceto nas situações mais triviais.) Apenas como exemplo, qualquer programador de jogos usaria categorias como as seguintes em todos os projetos, como uma questão básica. Observe que neste exemplo (acontece) usando uma função (Mathf.Approximately) que é incorporada ao ambiente; na prática, você normalmente precisa desenvolver cuidadosamente seus próprios conceitos sobre o que as comparações significam para representações computacionais de números reais, para o tipo de situação que você está criando. (Nem mencione que, se você estiver fazendo algo como, talvez um controlador, um controlador PID ou algo semelhante, todo o problema se torna central e muito difícil, torna-se a natureza do projeto.

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
fonte
1
Substitua if e mais porreturn (value >= Min && value <= Max);
AeroX 27/11
a maneira elegante de escrever a comparação é "em ordem lógica ..." if (Min <= value && value <= Max). Isso é muito mais bonito.
Fattie
2
Além dessa questão, é tão surpreendente que ninguém tenha mencionado a questão central em qualquer projeto do mundo real (principalmente se você é um engenheiro de jogos) é que você precisa lidar com a questão da aproximação . Em qualquer software do mundo real, você basicamente nunca "apenas faz uma comparação" (igualdade ou <,>), é necessário considerar e lidar com o problema do erro, dependendo da situação em questão. Editei em um adendo a esta resposta (a única resposta correta aqui!), Pois não são permitidas mais respostas.
Fattie
Obrigado por esta observação e pelo adendo.
Tony
2

Porque todas as outras respostas não são inventadas por mim, aqui apenas minha implementação:

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

Você pode usá-lo assim:

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

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

EDIT: Nova resposta fornecida. Eu estava começando a usar o C # quando escrevi a primeira resposta a essa pergunta e, em retrospectiva, agora percebo que minha "solução" era / é ingênua e ineficiente.

Minha resposta original: eu usaria a versão mais simples:

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

Uma maneira melhor

Como eu não vi nenhuma outra solução que seja mais eficiente (pelo menos de acordo com meus testes), tentarei outra vez.

Maneira nova e melhor que também funciona com intervalos 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);

Isso pode ser usado com faixas positivas e negativas e o padrão é uma faixa de

1..100 (inclusive) e usa xcomo o número a ser verificado seguido por um intervalo opcional definido por mine max.

Adicionando exemplos para uma boa medida

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

Devoluções:

True
True
True
False
True

Exemplo 2: Usando uma lista de entradas aleatórias entre 1 e 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);

Devoluções:

66660 ints found in range 1..100

Tempo de execução: 0,016 segundo (s)

cseder
fonte
Sim, estou comentando um comentário à minha resposta de 2013 :) @RyanTheLeach: Como minha resposta a essa pergunta é diferente da resposta agora "aceita"? Percebo que não é a travessia mais eficaz, mas “terrível”? Quão ruim pode ser a alocação e o loop de 100 ints? Em 1950 provavelmente não era socialmente aceito, mas ...
cseder
@RyanTheLeach Eu não culpo você ... Atualizei minha resposta; portanto, se você souber de uma solução ainda mais eficiente, por favor, elabore!
cseder 17/02
1
Excluí meus comentários porque eles não estão mais em pé. Obrigado pela correção, parece ok.
Ryan The Leach
1

Uma nova reviravolta em um antigo 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
fonte
3
Na verdade, existem quatro casos, inclusivo / inclusivo, inclusivo / exclusivo, exclusivo / inclusivo e exclusivo / exclusivo.
William T. Mallard
1

Em C, se a eficiência do tempo for crucial e o excesso de números inteiros for quebrado, seria possível if ((unsigned)(value-min) <= (max-min)) .... Se 'max' e 'min' forem variáveis ​​independentes, a subtração extra para (max-min) perderá tempo, mas se essa expressão puder ser pré-computada no tempo de compilação ou se puder ser calculada uma vez no tempo de execução para testar muitos números no mesmo intervalo, a expressão acima pode ser calculada com eficiência, mesmo no caso em que o valor esteja dentro do intervalo (se uma grande fração de valores estiver abaixo do intervalo válido, poderá ser mais rápido usar, if ((value >= min) && (value <= max)) ...pois sairá mais cedo se valor é menor que min).

Antes de usar uma implementação como essa, avalie a máquina de destino de uma pessoa. Em alguns processadores, a expressão em duas partes pode ser mais rápida em todos os casos, pois as duas comparações podem ser feitas independentemente, enquanto no método subtrair e comparar a subtração deve ser concluída antes que a comparação possa ser executada.

supercat
fonte
1

Que tal algo como isso?

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

com o método de extensão da seguinte forma (testado):

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

Eu faria um objeto Range, algo como isto:

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

Então você o usa desta maneira:

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

Dessa forma, você pode reutilizá-lo para outro tipo.

IEatBagels
fonte
Seu Rangeobjeto precisa usar o CompareTométodo para comparar itens, não o <operador.
Servy
Você está certo, embora, ao implementar o IComparable, você também deva substituir os operadores (pelo menos é o que minha análise de código do VS está dizendo), o que significa que <funcionaria. Embora eu possa estar errado, eu não tenho muita experiência e esta é a minha primeira resposta em SO
IEatBagels
Não, seu compilador não dirá que isso funciona. Isso não será compilado. É inteiramente razoável que um objeto implemente IComparablee não sobrecarregue o <operador.
Servy
1

Ao verificar se um "Número" está em um intervalo, você precisa ter uma noção clara do que você quer dizer, e o que dois números são iguais? Em geral, você deve agrupar todos os números de ponto flutuante no que é chamado de 'bola epsilon', para isso, escolha um valor pequeno e diga que se dois valores estão tão próximos, eles são a mesma coisa.

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

Com esses dois auxiliares no lugar e assumindo que, se algum número puder ser convertido em dobro, sem a precisão necessária. Tudo que você precisa agora é uma enumeração e outro método

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

O outro método segue:

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

Agora, isso pode ser muito mais do que o que você queria, mas impede que você lide com o arredondamento o tempo todo e tente se lembrar se um valor foi arredondado e em que lugar. Se necessário, você pode estendê-lo facilmente para trabalhar com qualquer epsilon e permitir que ele mude.

rahicks
fonte
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;

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

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

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

İBRAHİM GAZALOĞLU
fonte
1

Se você está preocupado com o comentário de @Daap na resposta aceita e só pode passar o valor uma vez, tente um dos seguintes

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

Em relação à elegância, a coisa mais próxima da notação matemática ( a <= x <= b ) melhora ligeiramente a legibilidade:

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

Para mais ilustrações:

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

Eu estava procurando uma maneira elegante de fazê-lo onde os limites pudessem ser alterados (ou seja, não tenho certeza de qual ordem os valores estão).

Isso funcionará apenas em versões mais recentes do C # onde existe o?:

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

Obviamente, você pode alterar os sinais = para seus propósitos. Também pode ser extravagante com a conversão de tipos. Eu só precisava de um retorno de flutuação dentro dos limites (ou igual a)

Kalikovision
fonte
0

Elegante porque não requer que você determine qual dos dois valores de limite é maior primeiro. Ele também não contém ramificações.

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
fonte
& + | são operadores bit a bit
nelsontruran
0

Eu não sei, mas eu 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));
}

E é assim que posso usá-lo:

    [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
fonte
Por favor, forneça um exemplo de uso abaixo do bloco de código, isso vai ajudar OP saber se ele se encaixa o seu propósito
Gabriel Balsa Cantú
0

Estes são alguns métodos de extensão que podem ajudar

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

Para validar os parâmetros do método, nenhuma das soluções lança ArgumentOutOfRangeException e permite a configuração fácil / adequada de valores mínimos / máximos inclusivos / exclusivos.

Use assim

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

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

Acabei de escrever essas belas funções. Ele também tem a vantagem de não ter ramificação (um único se) para valores válidos. A parte mais difícil é criar as mensagens de exceção adequadas.

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

Você está procurando in [1..100]? Isso é apenas Pascal.

Polluks
fonte
2
Não é verdade, não é apenas Pascal. Muitos idiomas modernos têm recursos como este. No Kotlin, por exemplo, é chamado de "Correspondência de padrões". Exemplo when (number) { in 0..9 -> println("1 digit") in 10..99 -> println("2 digits") in 100..999 -> println("3 digits") }
this.myself 13/03/19