Jak elegancko sprawdzić, czy liczba znajduje się w przedziale?

Jak to zrobić z C# i. NET 3.5/4?

Na przykład liczba może wynosić od 1 do 100.

Znam proste, jeśli wystarczy; ale kluczem do tego pytania jest elegancja. To do mojego projektu zabawek, a nie do produkcji.

To pytanie nie dotyczyło szybkości, Ale piękna kodu. Przestań mówić o efektywności i tym podobnych; pamiętaj, że wygłaszasz Kazania do chóru.

 114
Author: Peter Mortensen, 2010-07-06

20 answers

Istnieje wiele opcji:

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

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

Sprawdź również to więc post dla opcji regex.

 101
Author: Dustin Laine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:10

Masz na myśli?

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

Lub

bool TestRange (int numberToCheck, int bottom, int top)
{
  return (numberToCheck >= bottom && numberToCheck <= top);
}
 80
Author: kemiller2002,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-24 15:12:10

Aby dodać do szumu tutaj, możesz utworzyć metodę rozszerzenia:

public static bool IsWithin(this int value, int minimum, int maximum)
{
    return value >= minimum && value <= maximum;
}
Co pozwoli Ci zrobić coś takiego...
int val = 15;

bool foo = val.IsWithin(5,20);
Mówiąc to, wydaje się to Głupotą, gdy sam czek jest tylko jedną linijką.
 43
Author: Adam Robinson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-19 23:17:26

Jak mówili inni, użyj prostego if.

Powinieneś pomyśleć o zamówieniu.

E. g

1 <= x && x <= 100

Jest łatwiejszy do odczytania niż

x >= 1 && x <= 100
 37
Author: Esben Skov Pedersen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-25 16:04:15

Możesz zmniejszyć liczbę porównań z dwóch do jednego za pomocą matematyki. Idea jest taka, że jeden z dwóch czynników staje się ujemny, jeśli liczba leży poza zakresem i zero, jeśli liczba jest równa jednej z granic:

Jeśli granice są inkluzywne:

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

Lub

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

Jeśli granice są wyłączne:

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

Lub

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

Jednak w kodzie produkcyjnym po prostu napisałbym 1 < x && x < 100, jest to łatwiejsze do zrozumienia.

 31
Author: Olivier Jacot-Descombes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-06 15:00:12

Przy odrobinie nadużycia metody rozszerzenia możemy uzyskać następujące "eleganckie" rozwiązanie:

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!");
        }
    }
}
 16
Author: Ferruccio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-06 18:12:19

Proponuję:

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

Przykłady:

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

I oczywiście ze zmiennymi:

myvalue.IsWithin(min, max)

Jest łatwy do odczytania (zbliżony do ludzkiego języka) i działa z dowolnym porównywalnym typem (integer, double, custom types...).

Posiadanie łatwego do odczytania kodu jest ważne, ponieważ programista nie zmarnuje "cykli mózgowych", aby go zrozumieć. W długich sesjach kodowania zmarnowane cykle mózgu sprawiają, że programista jest zmęczony wcześniej i podatny na błędy.

 12
Author: Anton M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-05 08:51:21

Jeśli jest to przypadkowe, wystarczy prosta if. Jeśli dzieje się tak w wielu miejscach, warto rozważyć te dwa:

  • PostSharp . Udekoruj metody atrybutami, które "wstrzykują" kod do metody po kompilacji. Nie wiem na pewno, ale wyobrażam sobie, że można go do tego użyć.

Coś w stylu:

[Between("parameter", 0, 100)]
public void Foo(int parameter)
{
}
  • Kod umowy . Ma tę zaletę, że ograniczenia mogą być sprawdzane w czasie kompilacji, przez statyczne weryfikacja Twojego kodu i miejsc, w których go używasz.
 7
Author: JulianR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-06 17:47:37
if (value > 1 && value < 100)
{
    // do work
}
else
{
    // handle outside of range logic
}
 5
Author: Nick Larsen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-06 17:33:34

Użycie wyrażenia && do połączenia dwóch porównań jest po prostu najbardziej eleganckim sposobem na to. Jeśli spróbujesz użyć wymyślnych metod rozszerzania i takich, napotkasz pytanie, czy uwzględnić górną granicę, dolną granicę, czy obie. Gdy zaczniesz dodawać dodatkowe zmienne lub zmieniać nazwy rozszerzeń, aby wskazać, co jest zawarte, Twój kod staje się dłuższy i trudniejszy do odczytania (dla zdecydowanej większości programistów). Ponadto narzędzia takie jak Resharper ostrzegą cię, jeśli Twoje porównanie nie ma sensu (number > 100 && number < 1), czego nie zrobią, jeśli użyjesz metody ('i.IsBetween(100, 1)').

Jedyną inną uwagą, którą chciałbym zrobić, jest to, że jeśli sprawdzasz wejścia z zamiarem wyrzucenia wyjątku, powinieneś rozważyć użycie kontraktów kodowych:

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

Jest to bardziej eleganckie niż if(...) throw new Exception(...) i możesz nawet uzyskać ostrzeżenia w czasie kompilacji, jeśli ktoś spróbuje wywołać Twoją metodę bez upewnienia się, że numer jest w granicach jako pierwszy.

 5
Author: StriplingWarrior,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-06 19:16:33

Jeśli chcesz napisać więcej kodu niż prosty if, może możesz: Utwórz metodę rozszerzenia o nazwie 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());

Addendum: Warto zauważyć, że w praktyce bardzo rzadko" sprawdzasz równość " (lub) w bazie kodowej. (Inne niż w najbardziej trywialnych sytuacjach.) Czysto jako przykład, każdy programista gier użyłby kategorii czegoś takiego jak poniżej w każdym projekcie, jako podstawowej kwestii. Zauważ, że w tym przykładzie (dzieje się to be) za pomocą funkcji (Mathf.W przybliżeniu), który jest wbudowany w to środowisko; w praktyce zazwyczaj trzeba starannie opracować własne koncepcje tego, co oznacza porównanie dla komputerowych reprezentacji liczb rzeczywistych, dla rodzaju sytuacji, którą się projektuje. (Nie wspominaj nawet, że jeśli robisz coś takiego, może kontroler, kontroler PID lub tym podobne, cała sprawa staje się Centralna i bardzo trudna, staje się naturą projektu.) Bynajmniej nie jest OP pytanie tutaj pytanie trywialne lub nieistotne.

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;
    }
 2
Author: Tony,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-02 04:39:57

Ponieważ wszystkie inne odpowiedzi nie są wymyślone przeze mnie, tutaj tylko moja realizacja:

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

Możesz użyć go w następujący sposób:

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

var result = value.IsWithin(start, end, Range.Closed);
 2
Author: Oliver,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-07 08:42:54

Nowy twist na starym ulubionym:

public bool IsWithinRange(int number, int topOfRange, int bottomOfRange, bool includeBoundaries) {
    if (includeBoundaries)
        return number <= topOfRange && number >= bottomOfRange;
    return number < topOfRange && number > bottomOfRange;
}
 1
Author: Ben Hoffstein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-06 17:42:39

W C, jeśli wydajność czasu jest kluczowa i całkowite przepełnienia zawijają się, można zrobić if ((unsigned)(value-min) <= (max-min)) .... Jeśli ' max ' i ' min ' są zmiennymi niezależnymi, dodatkowe odejmowanie dla (max-min) będzie stratą czasu, ale jeśli to wyrażenie może być wstępnie obliczone w czasie kompilacji, lub jeśli można je obliczyć raz w czasie wykonywania, aby przetestować wiele liczb w tym samym zakresie, powyższe wyrażenie może być obliczone efektywnie nawet w przypadku, gdy wartość znajduje się w zakresie (jeśli duża część wartości będzie poniżej wartości poprawnej). zakres, może być szybsze użycie if ((value >= min) && (value <= max)) ..., Ponieważ zakończy się wcześniej , Jeśli wartość jest mniejsza niż min).

Zanim jednak użyjesz takiej implementacji, porównaj ją z docelową maszyną. W niektórych procesorach wyrażenie dwuczęściowe może być szybsze we wszystkich przypadkach, ponieważ dwa porównania mogą być wykonane niezależnie, podczas gdy w metodzie subtract-and-compare odejmowanie musi zostać zakończone, zanim porównanie może zostać wykonane.

 1
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-23 11:55:53

Może coś takiego?

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

Z następującą metodą rozszerzenia (badaną):

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;
    }
}
 1
Author: William T. Mallard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-28 16:52:17

Zrobiłbym obiekt Range, coś takiego:

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

Następnie używasz go w ten sposób:

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

W ten sposób możesz użyć go ponownie dla innego typu.

 1
Author: IEatBagels,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-19 23:17:48

Elegancki, ponieważ nie wymaga określenia, która z dwóch wartości granicznych jest większa jako pierwsza. Nie zawiera również rozgałęzień.

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 );
}
 1
Author: Tom Leys,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-07 02:27:13

Wybrałbym bardziej prostą wersję:

if(Enumerable.Range(1,100).Contains(intInQuestion)) { ...DoStuff; }
 0
Author: cseder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-08 08:43:30

Szukałem eleganckiego sposobu, aby to zrobić, gdzie granice mogą być przełączane (tj. Nie wiem w jakiej kolejności są te wartości).

To będzie działać tylko na nowszych wersjach C# gdzie: exists

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

Oczywiście możesz zmienić tam znaki = dla swoich celów. Może się też spodobać z odlewaniem typu. I just needed a float return within bounds (or equal to)

 0
Author: Kalikovision,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-13 13:17:38

Sprawdzając, czy "liczba" jest w zakresie, musisz być jasne, co masz na myśli i co oznaczają dwie liczby równe? Ogólnie rzecz biorąc, należy zawinąć wszystkie liczby zmiennoprzecinkowe w tzw. "kulę epsilona", co odbywa się poprzez wybranie małej wartości i stwierdzenie, że jeśli dwie wartości są tak bliskie, to są takie same.

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

Z tymi dwoma pomocnikami na miejscu i zakładając, że jeśli dowolna liczba może być rzucona jako Podwójna bez wymaganej dokładności. Wszystko, czego teraz potrzebujesz, to enum i another method

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

Druga metoda jest następująca:

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

Teraz może to być znacznie więcej niż to, co chciałeś, ale to powstrzymuje cię od czynienia z zaokrąglania cały czas i stara się pamiętać, czy wartość została zaokrąglona i w jakim miejscu. Jeśli potrzebujesz, możesz łatwo rozszerzyć to na pracę z dowolnym epsilonem i umożliwić zmianę epsilonu.

 0
Author: rahicks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-22 15:21:02