如何优雅地检查一个数字是否在一个范围内?

时间:2010-07-06 17:30:23

标签: c# numbers int

如何使用C#和.NET 3.5 / 4优雅地完成此任务?

例如,数字可以介于1到100之间。

我知道一个简单的if就足够了;但这个问题的关键词是优雅。这是我的玩具项目不是为了生产。

这个问题不是关于速度,而是关于代码美。停止谈论效率等等;记住你正在向合唱团讲道。

27 个答案:

答案 0 :(得分:114)

有很多选择:

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

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

另外,请查看此SO post以了解正则表达式选项。

答案 1 :(得分:83)

你的意思是?

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

bool TestRange (int numberToCheck, int bottom, int top)
{
  return (numberToCheck >= bottom && numberToCheck <= top);
}

答案 2 :(得分:51)

只是为了添加噪音,你可以创建一个扩展方法:

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

这会让你做一些像......

int val = 15;

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

话虽如此,当支票本身只有一行时,这似乎是一件愚蠢的事情。

答案 3 :(得分:42)

正如其他人所说,使用简单的if。

你应该考虑订购。

e.g

1 <= x && x <= 100

更容易阅读
x >= 1 && x <= 100

答案 4 :(得分:35)

您可以使用一些数学将比较次数从2减少到1。这个想法是,如果数字位于范围之外,则两个因子中的一个变为负数,如果数字等于其中一个边界则为零:

如果界限是包容性的:

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

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

如果边界是独占的:

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

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

但是,在生产代码中我只想写1 < x && x < 100,它更容易理解。

答案 5 :(得分:16)

通过一些扩展方法滥用,我们可以获得以下“优雅”的解决方案:

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

答案 6 :(得分:14)

我建议:

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

示例:

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

当然还有变量:

myvalue.IsWithin(min, max)

它易于阅读(接近人类语言)并适用于任何类似的类型(整数,双重,自定义类型......)。

让代码易于阅读非常重要,因为开发人员不会浪费大脑周期&#34;了解它。在长时间的编码会话中,浪费的脑循环会使开发人员更早地感到疲

答案 7 :(得分:7)

如果这是偶然的,那么您只需要一个简单的if。如果在许多地方发生这种情况,您可能需要考虑这两个:

  • PostSharp。使用在编译后将代码“注入”到代码中的属性来装饰方法。我不确定,但我可以想象它可以用于此。

类似的东西:

[Between("parameter", 0, 100)]
public void Foo(int parameter)
{
}
  • Code contracts。具有以下优点:可以在编译时通过静态验证代码和使用代码的位置来检查约束。

答案 8 :(得分:5)

if (value > 1 && value < 100)
{
    // do work
}
else
{
    // handle outside of range logic
}

答案 9 :(得分:5)

使用&&表达式加入两个比较只是最优雅的方法。如果您尝试使用花哨的扩展方法等,则会遇到是否包含上限,下限或两者的问题。一旦开始添加其他变量或更改扩展名以指示包含的内容,您的代码就会变得更长,更难以阅读(对于绝大多数程序员而言)。此外,像Resharper这样的工具会警告你,如果你的比较没有意义(number > 100 && number < 1),如果你使用一种方法('i.IsBetween(100,1)')他们就不会这样做。

我要做的唯一其他评论是,如果您要检查输入是否有意图抛出异常,您应该考虑使用代码合同:

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

这比if(...) throw new Exception(...)更优雅,如果有人试图调用你的方法而不确保数字首先在边界内,你甚至可以获得编译时警告。

答案 10 :(得分:2)

如果你想编写更多的代码而不是简单的if,也许你可以: 创建一个名为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());

附录:值得注意的是,在实践中,您很少“仅仅检查代码库中的相等性”(或&lt;,&gt;)。 (除了在最微不足道的情况下。)纯粹作为一个例子,任何游戏程序员都会在每个项目中使用类似下面的类别作为基本事项。请注意,在此示例中,它(恰好是)使用内置于该环境的函数(Mathf.Approximately);在实践中,您通常必须仔细开发自己的概念,比较对于实际数字的计算机表示,对于您正在设计的情况类型。 (甚至不要提到,如果你做的事情,可能是控制器,PID控制器等,整个问题变得中心而且非常困难,它就成了项目的本质。)绝不是OP这里提出一个微不足道或无关紧要的问题。

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

答案 11 :(得分:2)

因为所有其他答案都不是我发明的,这里只是我的实现:

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

然后您可以像这样使用它:

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

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

答案 12 :(得分:1)

我会做一个Range对象,如下所示:

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

然后你这样使用它:

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

这样你可以将它重用于另一种类型。

答案 13 :(得分: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);
    }
}

用法

double numberToBeChecked = 7;

var结果= numberToBeChecked.IsBetween(100,122);

var结果= 5.IsBetween(100,120);

var结果= 8.0.IsBetween(1.2,9.6);

答案 14 :(得分:1)

如果您担心@Daap对已接受答案的评论,并且只能传递一次值,则可以尝试以下操作之一

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

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

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

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

答案 15 :(得分:1)

关于优雅,最接近数学符号( a <= x <= b )的可读性稍有提高:

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

进一步的说明:

public static bool IsOutside(this int value, int min, int max)
{
    return value < min || max < value;
}

答案 16 :(得分:1)

这样的事情怎么样?

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

使用扩展方法如下(测试):

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

答案 17 :(得分:1)

在C中,如果时间效率至关重要且整数溢出将换行,则可以if ((unsigned)(value-min) <= (max-min)) ...。如果'max'和'min'是独立变量,(max-min)的额外减法将浪费时间,但是如果该表达式可以在编译时预先计算,或者如果它可以在运行时计算一次以测试许多对于相同范围的数字,即使在值在范围内的情况下也可以有效地计算上述表达式(如果大部分值将低于有效范围,则使用if ((value >= min) && (value <= max)) ...可能更快,因为它如果值小于分钟,提前退出

在使用这样的实现之前,先测试一个人的目标机器。在某些处理器上,两部分表达式在所有情况下都可能更快,因为两次比较可以独立完成,而在减法和比较方法中,减法必须在比较可以执行之前完成。

答案 18 :(得分:1)

旧的喜爱的新转折:

public bool IsWithinRange(int number, int topOfRange, int bottomOfRange, bool includeBoundaries) {
    if (includeBoundaries)
        return number <= topOfRange && number >= bottomOfRange;
    return number < topOfRange && number > bottomOfRange;
}

答案 19 :(得分:0)

如果要验证方法参数,则所有解决方案都不会抛出ArgumentOutOfRangeException并允许轻松/适当地配置包含/不含的最小值/最大值。

像这样使用

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

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

我只是写了这些漂亮的函数。它还具有不对有效值进行分支(单个if)的优点。最难的部分是制作适当的异常消息。

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

答案 20 :(得分:0)

这些是一些可以帮助您的扩展方法

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

答案 21 :(得分:0)

我不知道,但是我使用这种方法:

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

这是我可以使用它的方式:

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

答案 22 :(得分:0)

优雅,因为它不需要您首先确定两个边界值中的哪一个更大。它也没有分支。

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

答案 23 :(得分:0)

当检查“数字”是否在一个范围内时,你必须清楚你的意思,两个数字相等是什么意思?一般来说,你应该将所有浮点数包装在所谓的'epsilon ball'中,这是通过选取一些小值并说两个值是否接近它们是完全相同的。

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

将这两个助手放在适当的位置并假设如果任何数字可以在不具有所需精度的情况下转换为双精度。你现在需要的只是一个枚举和另一种方法

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

另一种方法如下:

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

现在这可能比你想要的要多得多,但是它让你无法一直处理四舍五入并试图记住一个值是否被舍入到了什么位置。如果您需要,您可以轻松扩展它以适用于任何epsilon并允许您的epsilon更改。

答案 24 :(得分:0)

我一直在寻找一种优雅的方法,可以切换边界(即不确定值的顺序)。

这只适用于较新版本的C#,其中?:exists

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

显然你可以为了你的目的改变那里的=标志。也可以使用类型铸造。我只需要在范围内(或等于)

进行浮点返回

答案 25 :(得分:0)

我会选择更简单的版本:

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

答案 26 :(得分:-1)

您要寻找in [1..100]吗?那只是帕斯卡。