4

是否有可能(在 C# 中)使checked(...)表达式具有用于溢出检查的动态“范围”?换句话说,在以下示例中:

int add(int a, int b)
{
    return a + b;
}
void test()
{
    int max = int.MaxValue;
    int with_call = checked(add(max, 1)); // does NOT cause OverflowException
    int without_call = checked(max + 1);  // DOES cause OverflowException
}

因为在表达式checked(add(max, 1))中,函数调用OverflowException会导致溢出,即使在表达式的动态范围内发生溢出,也会抛出no checked(...)

有没有办法让两种方式都评估int.MaxValue + 1抛出一个OverflowException

编辑:好吧,要么告诉我是否有办法,要么给我一个更好的方法来做到这一点(请)。

我认为我需要这个的原因是因为我有如下代码:

void do_op(int a, int b, Action<int, int> forSmallInts, Action<long, long> forBigInts)
{
    try
    {
        checked(forSmallInts(a, b));
    }
    catch (OverflowException)
    {
        forBigInts((long)a, (long)b);
    }
}
...
do_op(n1, n2, 
    (int a, int b) => Console.WriteLine("int: " + (a + b)),
    (long a, long b) => Console.WriteLine("long: " + (a + b)));

我希望打印int: ...它是否a + bint范围内,并且long: ...如果小整数加法溢出。有没有比简单地改变每一个Action(我有很多)更好的方法?

4

4 回答 4

5

简而言之,检查块或表达式不可能具有动态范围。如果您想在整个代码库中应用它,您应该考虑将它添加到您的编译器选项中。

应在实际发生操作的地方使用检查表达式或检查块。

    int add(int a, int b)
    {
        int returnValue = 0;

        try
        {
            returnValue = checked(a + b);
        }
        catch(System.OverflowException ex)
        {
            //TODO: Do something with exception or rethrow
        }

        return returnValue;
    }

    void test()
    {
        int max = int.MaxValue;
        int with_call = add(max, 1);
    }
于 2013-08-20T14:01:04.370 回答
4

您不应该将异常作为程序自然流程的一部分来捕获。相反,您应该预见到问题。有很多方法可以做到这一点,但假设你只关心intlong何时加法溢出:

编辑:使用您在评论中提到的类型而不是intand long

void Add(RFSmallInt a, RFSmallInt b)
{
    RFBigInt result = new RFBigInt(a) + new RFBigInt(b);
    Console.WriteLine(
        (result > RFSmallInt.MaxValue ? "RFBigInt: " : "RFSmallInt: ") + result);   
}

这假设您有一个构造函数来RFBigInt提升RFSmallInt. 这应该是微不足道的,因为BigInteger对于long. 如果值没有溢出,还有一个显式转换 from BigIntegerto ,您可以使用它来“降级”该值。long

于 2013-08-14T23:19:55.207 回答
1

异常应该是异常,而不是通常的程序流程。但是现在让我们不关心这个:)

我相信对你的问题的直接回答是否定的,但你总是可以自己解决这个问题。我发布了我在实现无界整数(实际上是整数的链接列表)时制作的一些忍者东西的一小部分,这可以帮助你。

如果性能不是问题,这是手动进行检查添加的一种非常简单的方法。如果您可以重载类型的运算符,即您控制类型,那就太好了。

public static int SafeAdd(int left, int right)
{
if (left == 0 || right == 0 || left < 0 && right > 0 || right < 0 && left > 0)
    // One is 0 or they are both on different sides of 0
    return left + right;
else if (right > 0 && left > 0 && int.MaxValue - right > left)
    // More than 0 and ok
    return left + right;
else if (right < 0 && left < 0 && int.MinValue - right < left)
    // Less than 0 and ok
    return left + right;
else
    throw new OverflowException();
}

使用您自己的类型的示例:

public struct MyNumber 
{
  public MyNumber(int value) { n = value; }

  public int n; // the value

  public static MyNumber operator +(MyNumber left, MyNumber right)
  {
    if (left == 0 || right == 0 || left < 0 && right > 0 || right < 0 && left > 0)
      // One is 0 or they are both on different sides of 0
      return new MyNumber(left.n + right.n); // int addition
    else if (right > 0 && left > 0 && int.MaxValue - right > left)
      // More than 0 and ok
      return new MyNumber(left.n + right.n); // int addition
    else if (right < 0 && left < 0 && int.MinValue - right < left)
      // Less than 0 and ok
      return new MyNumber(left.n + right.n); // int addition
    else
      throw new OverflowException();
  }

  // I'm lazy, you should define your own comparisons really
  public static implicit operator int(MyNumber number) { return number.n; }
}

正如我之前所说,您将失去性能,但会获得例外。

于 2013-08-19T21:35:43.737 回答
1

您可以使用表达式树并对其进行修改以引入 Checked for math 运算符并执行它。此示例未经编译和测试,您将不得不对其进行更多调整。

   void  CheckedOp (int a, int b, Expression <Action <int, int>> small, Action <int, int> big){
         var smallFunc = InjectChecked (small);
         try{
               smallFunc(a, b);
         }catch (OverflowException oe){
               big(a,b);
         }
   }


   Action<int, int> InjectChecked( Expression<Action<int, int>> exp )
   {
          var v = new CheckedNodeVisitor() ;
          var r = v.Visit ( exp.Body);
          return ((Expression<Action<int, int>> exp) Expression.Lambda (r, r. Parameters) ). Compile() ;
   }


   class CheckedNodeVisitor : ExpressionVisitor {

           public CheckedNodeVisitor() {
           }

           protected override Expression VisitBinary( BinaryExpression be ) {
                  switch(be.NodeType){
                        case ExpressionType.Add:   
                                return Expression.AddChecked( be.Left, be.Right);
                  }
                  return be;
           }
   }
于 2013-08-25T05:45:48.733 回答