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

Ser*_*pia 141 c# int numbers

如何使用C#和.NET 3.5/4优雅地完成这项工作?

例如,数字可以在1到100之间.

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

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

Dus*_*ine 128

有很多选择:

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

if (x >= 1 && x <= 100)
    //true
Run Code Online (Sandbox Code Playgroud)

另外,请查看此SO帖子以获取正则表达式选项.

  • Enumerable.Range必须首先生成可枚举的整数,然后遍历每个项目以找到它.与检查值相比,这是一个糟糕的想法和性能是截然不同的.我认为我们应该采用moto,因为LINQ Extensions很酷,并不意味着它们应该被用于所有东西. (298认同)
  • @马修:http://stackoverflow.com/questions/777400/what-is-the-biggest-mistake-people-make-when-starting-to-use-linq/777412#777412 (13认同)
  • 我同意这在性能方面是一个糟糕的想法,但是OP希望比"if"声明更有趣.这当然实现了......;) (13认同)
  • 值得注意的是,第二个参数不是"停止",而是"计数".例如,Enumerable.Range(150,300).Contains(400)将返回true. (7认同)
  • 这是一个令人震惊的解决方案 ** 不优雅**。当然,优雅的解决方案是一个类别……一个扩展……就像托尼的回答一样。这是在 c# 和大多数语言中编码此类问题的优雅方式。 (3认同)
  • *请不要使用此答案*.如果您的范围非常大,它将具有可怕的性能.请参阅@ olivier-jacot-descombes的答案 (3认同)
  • 这个问题的关键不在于讨论执行过程中哪种方式更快.这是什么代码看起来更好,仍然完成工作(再次:我不关心性能); 这个胜利到目前为止.易于阅读和易于使用. (2认同)
  • 它不优雅,超级慢.-1 (2认同)
  • 更容易阅读?!`if(x &gt;= 1 &amp;&amp; x &lt;= 100)` 比试图弄清楚第一个说的是什么要明显得多。 (2认同)
  • 更容易阅读,对于“范围内”,`(1 &lt;= x &amp;&amp; x &lt;= 100)`,以及对于“范围外”`(x &lt; 1 &amp;&amp; 100 &lt; x)`。 (2认同)

kem*_*002 88

你的意思是?

if(number >= 1 && number <= 100)
Run Code Online (Sandbox Code Playgroud)

要么

bool TestRange (int numberToCheck, int bottom, int top)
{
  return (numberToCheck >= bottom && numberToCheck <= top);
}
Run Code Online (Sandbox Code Playgroud)

  • @Ben,等到我试着申请专利:) (4认同)

Ada*_*son 52

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

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

哪个会让你做点什么......

int val = 15;

bool foo = val.IsWithin(5,20);
Run Code Online (Sandbox Code Playgroud)

话虽这么说,当检查本身只有一行时,这似乎是一件愚蠢的事情.


Esb*_*sen 45

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

你应该考虑订购.

例如

1 <= x && x <= 100
Run Code Online (Sandbox Code Playgroud)

比阅读更容易阅读

x >= 1 && x <= 100
Run Code Online (Sandbox Code Playgroud)

  • "更容易"是旁观者的眼睛.我个人更喜欢左边有变量,右边有常量或变量*不*. (17认同)
  • 在[Perl 6](http://dev.perl.org/perl6/rfc/25.html)中,你会写出`1 <= x <= 100`. (13认同)
  • 我也喜欢数字行顺序,也适用于补码测试,例如x <10 || 20 <x.对我来说,它喊"x超出范围10 - 20". (3认同)
  • 数字行顺序最初是最清晰的 - 但您可以训练您的眼睛/头脑以获得其他订单.具体来说 - 我喜欢将_constant_放在左边的技巧.如果你这样做,编译器会在你键入`=`而不是`==`时告诉你.它对非等式关系运算符没有帮助 - 但很容易习惯一致地使用它. (2认同)

Oli*_*bes 35

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

如果界限是包容性的:

(x - 1) * (100 - x) >= 0
Run Code Online (Sandbox Code Playgroud)

要么

(x - min) * (max - x) >= 0
Run Code Online (Sandbox Code Playgroud)

如果边界是独占的:

(x - 1) * (100 - x) > 0
Run Code Online (Sandbox Code Playgroud)

要么

(x - min) * (max - x) > 0
Run Code Online (Sandbox Code Playgroud)

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

  • 根据我的标准,这是迄今为止最优雅的解决方案,有趣的是,对我而言,它似乎比检查两个表达式运行速度稍慢,表示它似乎更不一致(速度似乎变化更大)会很有趣如果有任何研究在哪一个更快. (3认同)
  • 在javascript上测试您的解决方案,并使用最多14位小数的浮点数精确测试.这是一个非常好的代码片段.如果可以的话,它会给你三倍的投票 (3认同)
  • 但是,如果涉及大的正数,则存在一个小问题,它可能会溢出!XD您可能希望在编写代码时牢记这一点. (3认同)
  • 这个问题要求优雅,因此具有更多的学术意义而不是实用价值。我个人将只在生产代码中使用简单的`1 &lt;x &amp;&amp; x &lt;100'。它更容易理解。 (2认同)
  • 对于那些关心性能的人,`1 &lt; x &amp; x &lt; 100`(没有 &amp;&amp; 短路)指示编译器无论`1 &lt; x`的结果如何,它总是可以评估`x &lt; 100`。奇怪的是(由于分支预测)总是做这个简单的操作比有时跳过它要快。 (2认同)
  • 分支预测仅在使用短路“&amp;&amp;”评估时有用,因为“&amp;”不涉及分支。 (2认同)

Fer*_*cio 18

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

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!");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Ant*_*n M 17

我建议这个:

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;
}
Run Code Online (Sandbox Code Playgroud)

例子:

45.IsWithin(32, 89)
true
87.2.IsWithin(87.1, 87.15)
false
87.2.IsWithin(87.1, 87.25)
true
Run Code Online (Sandbox Code Playgroud)

当然还有变量:

myvalue.IsWithin(min, max)
Run Code Online (Sandbox Code Playgroud)

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

让代码易于阅读非常重要,因为开发人员不会浪费"脑循环"来理解它.在长时间的编码会话中,浪费的脑循环会使开发人员更早地感到疲

  • 我会通过使用单词之间更简化,并使用布尔标志来确定是否包含 (3认同)

Jul*_*anR 7

如果这是偶然的,只if需要一个简单的东西.如果在许多地方发生这种情况,您可能需要考虑以下两点:

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

就像是:

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


Nic*_*sen 5

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


Str*_*ior 5

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

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

Contract.Requires(number > 1 && number < 100)
Run Code Online (Sandbox Code Playgroud)

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

  • 仅供参考,当下限和上限约束被拆分为单独的Requires语句时,合同静态分析器会更快乐. (2认同)

cse*_*der 5

编辑:提供了新答案。当我写这个问题的第一个答案时,我刚刚开始使用 C#,事后我意识到我的“解决方案”是/是幼稚和低效的。

我的原始答案:我会选择更简单的版本:

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

更好的方式

由于我还没有看到任何其他更有效的解决方案(至少根据我的测试),我会再试一次。

新的更好的方法也适用于负范围

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

Run Code Online (Sandbox Code Playgroud)

这可以用于正范围和负范围,默认范围为

1..100(含)和用途 x用作要检查的数字,后跟由min和定义的可选范围max

添加示例以获得良好的度量

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

Run Code Online (Sandbox Code Playgroud)

返回:

True
True
True
False
True
Run Code Online (Sandbox Code Playgroud)

示例 2:使用 1 到 150 之间的 100000 个随机整数列表

True
True
True
False
True
Run Code Online (Sandbox Code Playgroud)

返回:

// 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);
Run Code Online (Sandbox Code Playgroud)