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

发布于 2024-09-08 06:07:22 字数 229 浏览 4 评论 0原文

我怎样才能用 C# 优雅地做到这一点?

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

我知道一个简单的 if (x >= 1 && x <= 100) 就足够了;但是随着 C#/.Net 不断添加大量语法糖和新功能,这个问题是关于更惯用的(可以很优雅)的编写方式。

性能不是问题,但请为非 O(1) 的解决方案添加性能说明,因为人们可能会复制粘贴建议。

How can I do this elegantly with C#?

For example, a number can be between 1 and 100.

I know a simple if (x >= 1 && x <= 100) would suffice; but with a lot of syntax sugar and new features constantly added to C#/.Net this question is about more idiomatic (one can all it elegance) ways to write that.

Performance is not a concern, but please add performance note to solutions that are not O(1) as people may copy-paste the suggestions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

洒一地阳光 2024-09-15 06:07:22

有很多选项:

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

实际上,可以在第一次检查中用相反的顺序更优雅地编写基本的 if

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

另外,请查看此 SO post 用于正则表达式选项。

注意:

  • LINQ 解决方案严格适用于样式点 - 因为 Contains 迭代所有项目,其复杂度为 O(range_size),而不是范围检查通常预期的 O(1)。
    其他范围的更通用版本(请注意第二个参数是计数,而不是结束):

    if (Enumerable.Range(start, end - start + 1).Contains(x)
    
  • 有人试图编写没有 &&if 解决方案,如 1 <= x <= 100 - 看起来非常优雅,但在 C# 中会导致语法错误“运算符 '<=' 无法应用于类型为 'bool' 和 'int' 的操作数”

There are a lot of options:

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

And indeed basic if more elegantly can be written with reversing order in the first check:

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

Also, check out this SO post for regex options.

Notes:

  • LINQ solution is strictly for style points - since Contains iterates over all items its complexity is O(range_size) and not O(1) normally expected from a range check.
    More generic version for other ranges (notice that second argument is count, not end):

    if (Enumerable.Range(start, end - start + 1).Contains(x)
    
  • There is temptation to write if solution without && like 1 <= x <= 100 - that look really elegant, but in C# leads to a syntax error "Operator '<=' cannot be applied to operands of type 'bool' and 'int'"

洋洋洒洒 2024-09-15 06:07:22
  1. 在生产代码中我只需编写

    <代码>1 <= x && x <= 100

    这很容易理解并且非常可读。

  2. 从 C#9.0 开始我们可以编写

    x >= 1 且 <= 100
    

    请注意,我们只能编写一次xis 引入了一个模式匹配表达式,其中 and 是模式的一部分。
    && 需要我们重复 x is,如 x is >= 1 && x 是 <= 100

    <前><代码>

  3. 这是一种巧妙的方法,通过使用一些数学方法将比较次数从两次减少到一次。这样做不一定有性能优势,但它很优雅。这个想法是,如果数字超出范围,则两个因素之一变为负值;如果数字等于边界之一,则两个因素之一变为零:

    如果边界包含在内:

    <前><代码>(x - 1) * (100 - x) >= 0

    <前><代码>(x - 最小值) * (最大值 - x) >= 0

    <小时>

    如果边界是互斥的:

    <前><代码> (x - 1) * (100 - x) > 0

    (x - 最小值) * (最大值 - x) > 0
    
  1. In production code I would simply write

    1 <= x && x <= 100

    This is easy to understand and very readable.

  2. Starting with C#9.0 we can write

    x is >= 1 and <= 100
    

    Note that we must write x only once. is introduces a pattern matching expression where and is part of the pattern.
    && would require us to repeat x is as in x is >= 1 && x is <= 100

  3. Here is a clever method that reduces the number of comparisons from two to one by using some math. There is not necessarily a performance advantage in doing so, but it is elegant. The idea is that one of the two factors becomes negative if the number lies outside of the range and zero if the number is equal to one of the bounds:

    If the bounds are inclusive:

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

    or

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

    If the bounds are exclusive:

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

    or

    (x - min) * (max - x) > 0
    
狼性发作 2024-09-15 06:07:22

你的意思是?

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

或者

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

Do you mean?

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

or

bool TestRange (int numberToCheck, int bottom, int top)
{
  return (numberToCheck >= bottom && numberToCheck <= top);
}
飘落散花 2024-09-15 06:07:22

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

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

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

Just to add to the noise here, you could create an extension method:

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

Which would let you do something like...

int val = 15;

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

That being said, this seems like a silly thing to do when the check itself is only one line.

何其悲哀 2024-09-15 06:07:22

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

你应该考虑一下顺序。

例如

1 <= x && x <= 100

x >= 1 && x <= 100

As others said, use a simple if.

You should think about the ordering.

e.g

1 <= x && x <= 100

is easier to read than

x >= 1 && x <= 100
り繁华旳梦境 2024-09-15 06:07:22

我建议这样:

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)

它很容易阅读(接近人类语言)并且适用于任何类似的类型(整数、双精度、自定义类型...)。

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

I propose this:

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

Examples:

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

and of course with variables:

myvalue.IsWithin(min, max)

It's easy to read (close to human language) and works with any comparable type (integer, double, custom types...).

Having code easy to read is important because the developer will not waste "brain cycles" to understand it. In long coding sessions wasted brain cycles make developer tired earlier and prone to bug.

心意如水 2024-09-15 06:07:22

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

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

With a bit of extension method abuse, we can get the following "elegant" solution:

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!");
        }
    }
}
无法回应 2024-09-15 06:07:22

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

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

类似于:

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

If this is incidental, a simple if is all you need. If this happens in many places, you might want to consider these two:

  • PostSharp. Decorate methods with attributes that 'inject' code into the method after compilation. I don't know for sure, but I can imagine it can be used for this.

Something like:

[Between("parameter", 0, 100)]
public void Foo(int parameter)
{
}
  • Code contracts. Has the advantage that the constraints can be checked at compile time, by static verification of your code and the places that use your code.
著墨染雨君画夕 2024-09-15 06:07:22
if (value > 1 && value < 100)
{
    // do work
}
else
{
    // handle outside of range logic
}
if (value > 1 && value < 100)
{
    // do work
}
else
{
    // handle outside of range logic
}
同尘 2024-09-15 06:07:22

编辑:提供了新答案。
当我写下这个问题的第一个答案时,我刚刚开始使用 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 作为要检查的数字,后跟由 minmax 定义的可选范围。

添加良好措施的示例

示例 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 之间的 100000 个随机整数的列表

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

Execution Time: 0.016 second(s)

EDIT: New Answer provided.
I was just starting out using C# when I wrote the first answer to this question, and in hindsight I now realize that my "solution" was / is naive and inefficient.

My original answer:
I'd go with the more simple version:


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

A Better Way

As I haven't seen any other solution that is more efficient (according to my tests at least), I'll give it another go.

New and better way that also works with negative ranges:

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

This can be used with both positive and negative ranges and defaults to a range of

1..100 (inclusive) and uses x as the number to check followed by an optional range defined by min and max.

Adding Examples For Good Measure

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

Returns:

True
True
True
False
True

Example 2:
Using a list of 100000 random ints between 1 and 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);

Returns:

66660 ints found in range 1..100

Execution Time: 0.016 second(s)
东走西顾 2024-09-15 06:07:22

使用 && 表达式连接两个比较是执行此操作的最优雅的方法。如果您尝试使用奇特的扩展方法等,您会遇到是否包含上限、下限或两者的问题。一旦您开始添加其他变量或更改扩展名称以指示所包含的内容,您的代码就会变得更长且更难以阅读(对于绝大多数程序员而言)。此外,如果您的比较没有意义(number > 100 && number < 1),Resharper 等工具会向您发出警告,但如果您使用方法 ( 'i.IsBetween(100, 1)')。

我要说的唯一的其他评论是,如果您检查输入的目的是抛出异常,则应该考虑使用代码契约:

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

这比 if(...) throw new Exception(. ..),如果有人尝试调用您的方法而不首先确保数字在范围内,您甚至可能会收到编译时警告。

Using an && expression to join two comparisons is simply the most elegant way to do this. If you try using fancy extension methods and such, you run into the question of whether to include the upper bound, the lower bound, or both. Once you start adding additional variables or changing the extension names to indicate what is included, your code becomes longer and harder to read (for the vast majority of programmers). Furthermore, tools like Resharper will warn you if your comparison doesn't make sense (number > 100 && number < 1), which they won't do if you use a method ('i.IsBetween(100, 1)').

The only other comment I'd make is that if you're checking inputs with the intention to throw an exception, you should consider using code contracts:

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

This is more elegant than if(...) throw new Exception(...), and you could even get compile-time warnings if someone tries to call your method without ensuring that the number is in bounds first.

箜明 2024-09-15 06:07:22

好的,我也一起玩。已经有很多答案了,但也许还有一些其他新奇的空间:

(显然实际上并没有使用这些)

    var num = 7;
    const int min = 5;
    const int max = 10;
    var inRange = Math.Clamp(num, min, max) == num;

或者

    var num = 7;
    const int min = 5;
    const int max = 10;
    var inRange = num switch { < min => false, > max => false, _ => true };

或者

    var num = 7;
    const int min = 5;
    const int max = 10;
    var inRange = num is >= min and <= max;

好吧,也许你可以使用最后一个。

好的,再来一张

    var num = 7;
    const int min = 5;
    const int max = 10;
    var inRange = Enumerable.Range(min, max-min).Contains(num);

Ok I'll play along. So many answers already but maybe still room for some other novelties:

(obviously don't actually use these)

    var num = 7;
    const int min = 5;
    const int max = 10;
    var inRange = Math.Clamp(num, min, max) == num;

Or

    var num = 7;
    const int min = 5;
    const int max = 10;
    var inRange = num switch { < min => false, > max => false, _ => true };

Or

    var num = 7;
    const int min = 5;
    const int max = 10;
    var inRange = num is >= min and <= max;

OK maybe you could use that last one.

OK one more

    var num = 7;
    const int min = 5;
    const int max = 10;
    var inRange = Enumerable.Range(min, max-min).Contains(num);
日记撕了你也走了 2024-09-15 06:07:22

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

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

In C, if time efficiency is crucial and integer overflows will wrap, one could do if ((unsigned)(value-min) <= (max-min)) .... If 'max' and 'min' are independent variables, the extra subtraction for (max-min) will waste time, but if that expression can be precomputed at compile time, or if it can be computed once at run-time to test many numbers against the same range, the above expression may be computed efficiently even in the case where the value is within range (if a large fraction of values will be below the valid range, it may be faster to use if ((value >= min) && (value <= max)) ... because it will exit early if value is less than min).

Before using an implementation like that, though, benchmark one one's target machine. On some processors, the two-part expression may be faster in all cases since the two comparisons may be done independently whereas in the subtract-and-compare method the subtraction has to complete before the compare can execute.

扮仙女 2024-09-15 06:07:22
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);

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

Usage

double numberToBeChecked = 7;

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

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

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

饮湿 2024-09-15 06:07:22

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

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

These are some Extension methods that can help

  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;
    }
愿与i 2024-09-15 06:07:22

您可以使用模式匹配以最优雅的方式实现此目的:

int i = 5;
if(i is (>0 and <=10))
{

}

You can use pattern matching to achieve this in the most elegant way:

int i = 5;
if(i is (>0 and <=10))
{

}
我最亲爱的 2024-09-15 06:07:22

使用内置的 Range 结构(C# 8+),我们可以创建一个扩展方法来检查 Index 是否在原始范围内。

public static bool IsInRangeOf(this Range range, Index index)
{
   return index.Value >= range.Start.Value && index.Value < range.End.Value;
}

由于 Index 覆盖了隐式运算符,因此我们可以传递 int 而不是 Index 结构。

var range = new Range(1, 10);
var isInRange = range.IsInRangeOf(1); // true, 1..10 is inclusive min range index(1)
var isInRange = range.IsInRangeOf(10); // false, 1..10 exclusive on max range index (10).
var isInRange = range.IsInRangeOf(100); // false

Using the built in Range struct (C# 8+), we can create an extension method to check if an Index is within the original range.

public static bool IsInRangeOf(this Range range, Index index)
{
   return index.Value >= range.Start.Value && index.Value < range.End.Value;
}

Since Index overrides the implicit operator, we can pass an int instead of an Index struct.

var range = new Range(1, 10);
var isInRange = range.IsInRangeOf(1); // true, 1..10 is inclusive min range index(1)
var isInRange = range.IsInRangeOf(10); // false, 1..10 exclusive on max range index (10).
var isInRange = range.IsInRangeOf(100); // false

十秒萌定你 2024-09-15 06:07:22

如果您想编写比简单的 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());

附录的扩展方法,值得注意的是,在实践中,您很少在代码库中“仅检查相等性”(或 <、>)。 (除了在最琐碎的情况下。)纯粹作为一个例子,任何游戏程序员都会在每个项目中使用类似于以下内容的类别,作为基本问题。请注意,在这个示例中,它(碰巧)使用了内置于该环境中的函数(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;
    }

If you want to write more code than a simple if, maybe you can:
Create a Extension Method called 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: it's worth noting that in practice you very rarely "just check for equality" (or <, >) in a codebase. (Other than in the most trivial situations.) Purely as an example, any game programmer would use categories something like the following in every project, as a basic matter. Note that in this example it (happens to be) using a function (Mathf.Approximately) which is built in to that environment; in practice you typically have to carefully develop your own concepts of what comparisons means for computer representations of real numbers, for the type of situation you are engineering. (Don't even mention that if you're doing something like, perhaps a controller, a PID controller or the like, the whole issue becomes central and very difficult, it becomes the nature of the project.) BY no means is the OP question here a trivial or unimportant question.

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;
    }
把人绕傻吧 2024-09-15 06:07:22

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

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

Cause all the other answer are not invented by me, here just my implementation:

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

You can then use it like this:

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

var result = value.IsWithin(start, end, Range.Closed);
埋葬我深情 2024-09-15 06:07:22

旧的最爱的新变化:

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

A new twist on an old favorite:

public bool IsWithinRange(int number, int topOfRange, int bottomOfRange, bool includeBoundaries) {
    if (includeBoundaries)
        return number <= topOfRange && number >= bottomOfRange;
    return number < topOfRange && number > bottomOfRange;
}
淡淡的优雅 2024-09-15 06:07:22

像这样的事情怎么样?

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

How about something like this?

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

with the extension method as follows (tested):

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;
    }
}
乱了心跳 2024-09-15 06:07:22

我会做一个 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);

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

I would do a Range object, something like this:

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

Then you use it this way:

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

That way you can reuse it for another type.

情魔剑神 2024-09-15 06:07:22

当检查“数字”是否在某个范围内时,您必须清楚自己的意思,以及两个数字相等意味着什么?一般来说,您应该将所有浮点数包装在所谓的“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 更改。

When checking if a "Number" is in a range you have to be clear in what you mean, and what does two numbers are equal mean? In general you should wrap all floating point numbers in what is called a 'epsilon ball' this is done by picking some small value and saying if two values are this close they are the same thing.

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

With these two helpers in place and assuming that if any number can be cast as a double without the required accuracy. All you will need now is an enum and another method

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

The other method follows:

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

Now this may be far more than what you wanted, but it keeps you from dealing with rounding all the time and trying to remember if a value has been rounded and to what place. If you need to you can easily extend this to work with any epsilon and to allow your epsilon to change.

浅听莫相离 2024-09-15 06:07:22

优雅是因为它不需要您首先确定两个边界值中哪一个更大。它也不包含任何分支。

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

Elegant because it doesn't require you to determine which of the two boundary values is greater first. It also contains no branches.

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 );
}
ら栖息 2024-09-15 06:07:22

如果您担心 @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);

If you are concerned with the comment by @Daap on the accepted answer and can only pass the value once, you could try one of the following

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

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

or

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

//var t = TestRangeMargin(10, somelist.Count(), 5);
陌上青苔 2024-09-15 06:07:22

关于优雅,最接近数学符号 (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;
}

Regarding elegance, the closest thing to the mathematical notation (a <= x <= b) slightly improves readability:

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

For further illustration:

public static bool IsOutside(this int value, int min, int max)
{
    return value < min || max < value;
}
灰色世界里的红玫瑰 2024-09-15 06:07:22

使用 C# 8 和新的 System.Range struct 还有一个新选项,它利用新的 .. 运算符,但尚未提及。


首先为其创建一个扩展:

using System;

public static class RangeExtensions
{
    public static bool Contains(this Range range, int value) => range.Start.Value <= value && value < range.End.Value;
}

然后您可以将其应用到 Range 本身:

var isInRange = (1..11).Contains(2);

With C# 8 and the new System.Range struct there is one more new option that utilizes the new .. operator and hasn't been mentioned yet.


First create an extension for it:

using System;

public static class RangeExtensions
{
    public static bool Contains(this Range range, int value) => range.Start.Value <= value && value < range.End.Value;
}

Then you can apply it to the Range itself:

var isInRange = (1..11).Contains(2);
枕头说它不想醒 2024-09-15 06:07:22

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

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

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

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

I was looking for an elegant way to do it where the bounds might be switched (ie. not sure which order the values are in).

This will only work on newer versions of C# where the ?: exists

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

Obviously you could change the = signs in there for your purposes. Could get fancy with type casting too. I just needed a float return within bounds (or equal to)

想挽留 2024-09-15 06:07:22

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

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

I don't know but i use this method:

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

And this is the way I can use it:

    [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);
    }
姜生凉生 2024-09-15 06:07:22

如果要验证方法参数,则没有任何解决方案会抛出 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);

If it's to validate method parameters, none of the solutions throw ArgumentOutOfRangeException and allow easy/proper configuration of inclusive/exclusive min/max values.

Use like this

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

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

I just wrote these beautiful functions. It also has the advantage of having no branching (a single if) for valid values. The hardest part is to craft the proper exception messages.

/// <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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文