C#检查表达式的动态“范围”

时间:2013-08-14 22:34:07

标签: c# scope checked dynamic-scope

是否有可能(在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 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 + b范围内,我希望打印int,如果小整数添加溢出,我希望long: ...。有没有办法做到这一点,而不仅仅是改变每一个Action(我有很多)?

4 个答案:

答案 0 :(得分:5)

简而言之,检查块或表达式不可能具有动态范围。 如果您想在整个代码库中应用此功能,则应将其添加到compiler options

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

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

答案 1 :(得分:4)

作为程序自然流程的一部分,您不应该捕获异常。相反,你应该预见到这个问题。您可以通过多种方式执行此操作,但假设您只关心intlong以及何时添加溢出:

编辑:在评论中使用您在下面提到的类型,而不是intlong

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

这假设你有RFBigInt的构造函数来提升RFSmallInt。这应该是微不足道的,因为BigIntegerlong具有相同的效果。还有一个从BigIntegerlong的显式转换,如果值不溢出,您可以使用它来“降级”。

答案 2 :(得分: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; }
}

正如我之前所说,你将失去表现,但获得例外。

答案 3 :(得分:1)

你可以使用Expression Tree&amp;修改它以介绍Checked for math operator&amp;执行它。此示例未经过编译和测试,您将不得不进行更多调整。

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