【问题标题】:How to force a number to be in a range in C#? [duplicate]如何强制数字在 C# 中的范围内? [复制]
【发布时间】:2010-07-04 23:47:23
【问题描述】:

在 C# 中,我经常不得不将整数值限制为一个值范围。例如,如果应用程序需要百分比,则用户输入的整数不得小于零或大于一百。另一个例子:如果有五个网页通过Request.Params["p"] 访问,我期望的值是 1 到 5,而不是 0 或 256 或 99999。

我经常以写一个非常丑陋的代码结束,例如:

page = Math.Max(0, Math.Min(2, page));

甚至更丑:

percentage =
    (inputPercentage < 0 || inputPercentage > 100) ?
    0 :
    inputPercentage;

难道没有更聪明的方法在 .NET Framework 中做这些事情吗?

我知道我可以写一个通用方法int LimitToRange(int value, int inclusiveMinimum, int inlusiveMaximum)并在每个项目中使用它,但也许框架中已经有一个神奇的方法?

如果我需要手动执行此操作,那么在第一个示例中执行我正在执行的操作的“最佳”(即不那么丑且更快)的方式是什么?像这样?

public int LimitToRange(int value, int inclusiveMinimum, int inlusiveMaximum)
{
    if (value >= inclusiveMinimum)
    {
        if (value <= inlusiveMaximum)
        {
            return value;
        }

        return inlusiveMaximum;
    }

    return inclusiveMinimum;
}

【问题讨论】:

标签: c# .net performance math


【解决方案1】:

这个操作叫做'Clamp',通常写成这样:

public static int Clamp( int value, int min, int max )
{
    return (value < min) ? min : (value > max) ? max : value;
}

【讨论】:

  • 我喜欢Clamp这个名字。甚至 Microsoft.Xna.Framework 也有 Clamp 方法:)
  • Clamp 是正常名称。请参阅 OpenCL 规范。
  • 附言。此代码使用 = 比使用 更有效。 :-)
  • 添加'this',您可以将其用作扩展方法: public static int Clamp(this int value, int min, int max) { return (value 最大值)?最大值:值;现在你可以说:myInt.Clamp(10,50);
  • 从.NET Standard 2.1(Core 2.0)开始,内置Math.Clamp()方法。
【解决方案2】:

我看到了马克的回答,并通过this 提出:

public static class InputExtensions
{
    public static int LimitToRange(
        this int value, int inclusiveMinimum, int inclusiveMaximum)
    {
        if (value < inclusiveMinimum) { return inclusiveMinimum; }
        if (value > inclusiveMaximum) { return inclusiveMaximum; }
        return value;
    }
}

用法:

int userInput = ...;

int result = userInput.LimitToRange(1, 5)

见:Extension Methods

【讨论】:

  • 请注意,使用扩展方法不一定是好事。它只是另一种编程工具,它有它的用途。把它放在错误的地方或上下文中,你最终会写出不可读的代码。
  • 你的回答应该只是一个建议改进他的回答的评论。
【解决方案3】:

一种更简洁的方法,不仅可以处理整数(取自我自己的共享代码库):

public static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
{
    if (value.CompareTo(min) < 0)
        return min;
    if (value.CompareTo(max) > 0)
        return max;

    return value;
}

【讨论】:

  • 如果 valuenull,则会引发 NullReferenceException。最好使用 IComparer (例如Comparer&lt;T&gt;.Default)。 msdn.microsoft.com/en-us/library/azhsac5f.aspx
  • 从 C#8 开始,您可以在 IComparable 之后添加 notnull 以避免 dtb 指出的异常。 public static T Clamp&lt;T&gt;(T value, T min, T max) where T : IComparable&lt;T&gt;, notnull { // your choice of clamp method body }
  • 这很棒而且非常优雅,因为它确实涵盖了任何可比较的类型(例如 DateTime)而不仅仅是数字!
  • 您可以添加检查以确保 min 不大于 max。
  • 这里有一个稍微改进的版本:pastebin.com/0N0PaG6a
【解决方案4】:

我喜欢 Guffa 的回答,但令我惊讶的是,还没有人发布使用 Min/Max 的解决方案。

public int LimitInclusive(int value, int min, int max)
{
    return Math.Min(max, Math.Max(value, min));
}

【讨论】:

  • 也许每次使用MinMax 都会对性能产生影响?
  • 因为这里只支持整数和可以隐式转换成整数的类型
  • 感谢您发布此信息。它在我的系统上确实要快得多,如果您可以处理 int 数据类型,则应该使用它。我相信这是无分支的,并且会在处理器上使用经过数学​​优化的指令。
  • @OrestisP.:这是 OP 需要的。并非您编写的每个函数都需要能够处理您能想到的任何用例。这是浪费大量时间构建过度设计的混乱的好方法。
  • 实际上,Math.Max(和 Math.Min 也有)有 10 个重载;它还可以处理整数(intlonguint 等)和浮点数(floatdouble)。因此,如果 OP 也需要处理浮点数,它们可以重载 LimitInclusive
【解决方案5】:

编写LimitToRange 函数的另一种方法如下。

public int LimitToRange(int value, int inclusiveMinimum, int inclusiveMaximum)
{
    if (value < inclusiveMinimum) { return inclusiveMinimum; }
    if (value > inclusiveMaximum) { return inclusiveMaximum; }
    return value;
}

我认为这更容易理解,同时仍然有效。

【讨论】:

  • @MainMa:如果值在范围内,则无论您以何种顺序放置语句,都必须执行两次检查。性能方面,所有答案都应该相同。
  • 使用 = 可以避免第二次检查或在极端情况下跳转:-)
【解决方案6】:

不,框架中没有内置的任何方法。我想它被遗漏了,因为你已经有了MinMax,所以你可以使用它们来完成它。

如果您为它编写自己的方法,那么如何编写并不重要。如果您使用if 语句或条件运算符?,它仍然会编译成几乎相同的代码。

【讨论】:

  • 如果我们“已经拥有[工具]”,这听起来像是停止所有新的和/或更新编程语言的努力的论据! ...我们有工具可以编写我们自己的 Min Max 函数,为什么他们给我们这些??
【解决方案7】:

在不向用户提供任何关于他们输入的值错误的反馈的情况下限制值,一般来说,这可能不是一个好主意(恕我直言)。这可能会在以后导致难以调试的细微错误,尤其是在运行时确定最小值/最大值时。

想想这个。您的银行帐户中有 100 美元,您想将 150 美元转给您的朋友。您是否希望您的银行系统发送InsufficientFundsException 或与您的朋友讨论您转账了 150 美元但他只收到了 100 美元(假设银行将转账金额限制为 100,因为您没有足够的资金)

话虽如此,您还应该查看代码合同。

public void MyFunction (Type input)
{
   Contract.Requires(input > SomeReferenceValue);
   Contract.Requires (input < SomeOtherReferencValue);

}

这将强制用户输入在范围内。

【讨论】:

    【解决方案8】:

    我喜欢 Clamp 的名字。我会建议以下课程

    public class MathHelper
    {
        public static int Clamp (int value,int min,int max)
        {
              // todo - implementation 
        }
        public static float Clamp (float value,float min,float max)
        {
              // todo - implementation 
        }
    )
    

    或者如果你想使用泛型,那么

    public class MathHelper
    {
         public static T Clamp<T> (T value, T min, T max) where T : IComparable
         {
             // todo - implementation
             T output = value;
             if (value.CompareTo(max) > 0)
             {
                 return max;
             }
             if (value.CompareTo(min) < 0)
             {
                return min;
             }
            return  output;
         } 
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-07
      • 1970-01-01
      • 1970-01-01
      • 2011-12-22
      • 2022-11-10
      • 1970-01-01
      • 1970-01-01
      • 2016-01-23
      相关资源
      最近更新 更多