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


157

如何使用C#和.NET 3.5 / 4优雅地做到这一点?

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

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

这个问题不是关于速度,而是关于代码美。停止谈论效率之类的话题;记得你在向合唱团宣道。


23
回复:您的“编辑”- 简单优雅。我个人认为if语句比进行此检查的任何非标准方法都更优雅……
Reed Copsey

4
“一切都应该尽可能简单,但不要简单。” -爱因斯坦(Albert Einstein)
corsiKa 2010年

3
@Sergio:我不觉得自己是个书呆子。我觉得人们经常滥用该语言的扩展方法和其他工具来替换已经很简单的东西。比较两个int值的方法有数百种,但是使用IMO(最显而易见的选择)是一个比较差的选择。
里德·科普西

3
@Sergio:我想我看不出问题的重点了;)
Reed Copsey 2010年

6
@Sergio:如果if不是“巴洛克式”,请不要修复它。
StriplingWarrior 2010年

Answers:


154

有很多选择:

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

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

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


334
Enumerable.Range必须首先生成整数的可枚举,然后循环遍历每个项目以找到它。与检查值相比,这是一个糟糕的想法,并且性能大不相同。我认为我们应该采用moto,仅仅是因为LINQ Extensions很酷,并不意味着它们应该用于所有事情。
马修·阿伯特


15
我同意这是一个从性能角度考虑的糟糕想法,但是OP希望比if声明更花哨的东西。这肯定实现了...;)
蒂姆·科克

10
值得注意的是,第二个参数不是“ stop”,而是“ count”。例如,Enumerable.Range(150,300).Contains(400)将返回true。
Shathur

5
请不要使用这个答案。如果您的范围很大,它将具有可怕的性能。请查看@ olivier-jacot-descombes的答案
Aaron Hudon

95

你的意思是?

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

要么

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

1
您不需要在其中“是” ...它将不会编译。(否则,我同意100%)
里德·科普西

4
@Ben,请耐心等待,直到我也申请了专利:)
kemiller2002'7

我认为这是最可靠的解决方案,但发问者所寻求的并不是那么优雅,不是吗?
凯文·简

我唯一要更改的就是将static关键字添加到方法中。;-)
Robert S.

需要边界标记,即InRange(number,lowerBound,LOWER_IS_INCLUSIVE,Upperbound,UPPER_IS_EXCLUSIVE)允许<vs <=。我写这篇文章的意图是狡猾,但现在我考虑一下,这些标志实际上会鼓励调用者弄清楚它们的规范。
威廉·T·马拉德

56

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

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

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


1
@Ben:我继续讲这个主题,它说“在一定范围内”(在这方面我认为不是模棱两可的),但您说对了,问题主体说“ 1到100之间”(这是正确的) ,当然是big昧的)。
亚当·罗宾逊

48

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

您应该考虑订购。

例如

1 <= x && x <= 100

比起阅读更容易

x >= 1 && x <= 100

19
旁观者认为“更容易”。我个人更喜欢在左边有问题的变量,在右边有没有问题的常数或变量。
亚当·罗宾逊

15
Perl 6中,您可以编写1 <= x <= 100
Jordão酒店

2
号码线顺序最初是最清晰的-但是您可以训练自己的眼睛/头脑以了解其他顺序。具体来说-我总是喜欢将常量放在左侧的技巧。如果这样做,编译器会告诉您何时键入=而不是==。它对非等式关系运算符没有帮助-但很容易习惯于一致地使用它。
davidbak

1
我只想补充一点,这种解决方案在任何情况下都没有用。考虑x是一个复杂的函数调用或耗时的Linq表达式。在这种情况下,您将执行两次,这不是一件好事。当然,您应该将值存储到临时局部变量中,但是在某些情况下(例如,在else-if语句中),您只想在其他if或else-if失败之后调用函数。对于临时变量,您必须始终调用它们。在这些情况下,扩展方法(在其他答案中提到)是最好的解决方案。
罗伯特·S。

4
我也喜欢数字线顺序,也喜欢补码测试,例如x <10 || 20 <x。对我来说,它喊“ x不在10-20范围内”。
威廉·T·马拉德

44

在生产代码中,我只需写

1 <= x && x <= 100

这很容易理解,也很容易理解。


这是一个聪明的方法,它通过使用一些数学运算将比较次数从两个减少到一个。想法是,如果数字超出范围,则两个因素之一变为负数;如果数字等于界限之一,则两个因数变为零:

如果范围包括边界:

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

要么

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

如果边界是排他的:

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

要么

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

3
以我的标准来看,这是迄今为止最优雅的解决方案,有趣的是,对我来说,它似乎比检查两个表达式都快一些,也就是说,它似乎也更加不一致(速度似乎变化更大),这很有趣。如果有任何研究完成得更快。
Thomas Lindvall 2015年

3
在javascript上测试了您的解决方案,并以不超过14位小数的浮点数进行了准确的测试。这是一个非常好的代码段。如果可以的话,它会
投票

4
但是,如果涉及大的正数,则有一个小问题,它可能会溢出!XD在编写代码时,您可能要记住这一点。
BrainStorm.exe 2016年

2
这个问题要求优雅,因此更多的是学术性的,而不是实用的价值。就我个人而言,我只会使用简单1 < x && x < 100的生产性代码。它更容易理解。
奥利维尔·雅各布·德斯科姆斯

1
对于那些关心性能的人,1 < x & x < 100(无&&短路)指示编译器x < 100无论的结果如何都可以进行评估1 < x。奇怪的是(由于分支预测),始终执行此简单操作要比有时跳过它要快。
汤姆·莱斯

23

我建议:

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)

它易于阅读(接近人类语言),并且可以与任何可比较的类型(整数,双精度,自定义类型...)一起使用。

易于阅读的代码很重要,因为开发人员不会浪费“大脑周期”来理解它。在漫长的编码过程中,浪费的大脑周期使开发人员更早疲倦,并容易出现错误。


3
我将通过使用单词之间,并使用一个布尔标志来确定是否包含在内来进一步简化
Ben

好。很容易理解。我将名称更改为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可以使用,但我不喜欢负面条件
Paulustrious

21

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

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

喜欢的解决方案!顺便说一句,以支持包容性,创造enum Inclusive与价值:LowerUpperAll。并通过对In类型的功能一个附加参数enum Inclusive的默认值Inclusive.All,更新的To函数体来处理AllLowerUpper值:)
尼基塔

7

如果这是偶然的,if那么您只需要简单即可。如果在很多地方都发生这种情况,则可能需要考虑以下两个方面:

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

就像是:

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

+1代码合同;它特定于验证参数,但这是一个经常使用的案例,静态验证有可能会非常有用。
丹·布莱恩特


5

使用&&表达式将两个比较联接起来是最简单的方法。如果尝试使用类似的扩展方法,则会遇到是否要包含上限,下限或同时包含两者的问题。一旦开始添加其他变量或更改扩展名以指示所包含的内容,代码就会变得更长且更难阅读(对于大多数程序员而言)。此外,如果您的比较没有道理,Resharper之类的工具会警告您(number > 100 && number < 1),,如果您使用方法('i.IsBetween(100,1)'),则它们将不会。

我要发表的唯一其他评论是,如果要检查输入以引发异常,则应考虑使用代码协定:

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

这比更为优雅if(...) throw new Exception(...),如果有人尝试调用您的方法而没有确保该数字位于第一个范围内,您甚至可能会收到编译时警告。


2
仅供参考,将下限和上限约束拆分为单独的Requires语句时,合同静态分析器会更快乐。
丹·布莱恩特

感谢Dan Bryant,这正是我在这里寻找的东西。在关于Requires和其他相关Code Contract方法的条件样式建议上找不到太多的材料。
jpierson

2

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

附录:值得注意的是,在实践中,您很少在代码库中“仅检查是否相等”(或<,>)。(除了最琐碎的情况。)纯粹作为示例,任何游戏程序员都将在每个项目中使用类似于以下内容的类别作为基本问题。请注意,在本示例中,它(可能是)使用了内置于该环境中的函数(近似运算)。在实践中,您通常必须认真开发自己的概念,即比较对于实际数字的计算机表示形式,所要处理的情况类型的含义。(甚至不用说,如果您正在做某事,例如控制器,PID控制器之类的事情,整个问题就变得非常重要并且非常困难,这将成为项目的本质。

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

1
将if and else替换为return (value >= Min && value <= Max);
AeroX 2014年

如果(Min <= value && value <= Max),则编写比较的最佳方法是“按逻辑顺序...”。那真漂亮。
Fattie

2
进一步讲这个问题,令人惊讶的是,没有人提到任何实际项目中的核心问题(尤其是如果您是游戏工程师)是您必须处理近似问题。在任何实际的软件中,您基本上都不会“只是做比较”(无论是相等还是<,>),而必须根据当前情况来考虑并处理错误问题。由于不允许再有其他答案,因此我在此答案的附录中进行了编辑(这里是唯一正确的答案!)。
Fattie 2015年

感谢您的观察和附录。
Tony

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

2

编辑:提供了新的答案。当我写这个问题的第一个答案时,我才刚开始使用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);

既可以用于正值范围,也可以用于负值范围,默认范围为

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

返回值:

True
True
True
False
True

示例2:使用1到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);

返回值:

66660 ints found in range 1..100

执行时间:0.016秒


是的,我正在评论我2013年的回答:) @RyanTheLeach:我对这个问题的回答与现在“已接受”的回答有何不同?我意识到这不是最有效的遍历,而是“可怕”的?分配和遍历100个int会有多糟糕?在1950年,它可能未被社会接受,但是……
cseder

@RyanTheLeach我不怪你...我已经更新了答案,所以,如果您知道更有效的解决方案,请详细说明!
cseder

1
我已删除我的评论,因为它们不再存在。感谢您的修复,似乎还可以。
Ryan The Leach

1

旧收藏的新变化:

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

3
实际上有四种情况,包括/包含,包含/排除,排除/包含和排除/排除。
威廉·T·马拉德

1

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

但是,在使用这种实现之前,请先对目标计算机进行基准测试。在某些处理器上,两部分表达式在所有情况下都可能更快,因为两个比较可以独立完成,而在减法和比较法中,必须先完成减法才能执行比较。


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

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

这样,您可以将其重用于另一种类型。


您的Range对象需要使用该CompareTo方法来比较项目,而不是<运算符。
Servy 2013年

没错,尽管如果实现IComparable,还应该重写运算符(至少那是我的VS代码分析所要表达的意思),这意味着<可以工作。尽管我可能是错的,但是我没有太多经验,这是我的第一个答案
IEatBagels 2013年

不,您的编译器不会说这可行。这不会编译。一个对象的实现是完全合理的,IComparable并且不会使<操作员超载。
Servy 2013年

1

在检查“数字”是否在范围内时,必须清楚您的意思,两个数字相等是什么意思?通常,您应该将所有浮点数包装在所谓的“ε球”中,方法是选择一些较小的值,然后说两个值是否接近,这是同一件事。

    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。


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 result = numberToBeChecked.IsBetween(100,122);

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

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


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

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

0

我一直在寻找一种优雅的方法来完成可能会切换边界的操作(即不确定值的顺序)。

这仅适用于存在?:的C#的较新版本

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

显然,您可以根据需要更改其中的=符号。也可以使用类型转换。我只需要在(或等于)范围内返回浮点数


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

&+ | 是按位运算符
nelsontruran

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

请在代码块下面提供用法示例,这将帮助OP知道它是否符合他的目的
Gabriel BalsaCantú18年

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

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

-2

您在找in [1..100]什么?那只是帕斯卡。


2
不正确,不仅是Pascal。许多现代语言都具有这样的功能。例如,在Kotlin中,它称为“模式匹配”。示例 when (number) { in 0..9 -> println("1 digit") in 10..99 -> println("2 digits") in 100..999 -> println("3 digits") }
this.myself
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.