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

本文关键字:检查 检查表 表达式 动态 范围 | 更新日期: 2023-09-27 18:27:55

是否可能(在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)));

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

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

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

应在实际执行操作的位置使用已检查表达式或已检查块。

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

您不应该将异常捕获为程序自然流的一部分。相反,你应该预见到问题。有很多方法可以做到这一点,但假设您只关心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的显式强制转换,如果值没有溢出,您可以使用它来"降级"该值。

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

我相信你的问题的直接答案是否定的,但你总是可以解决这个问题。我发布了一小部分我在实现无界整数(实际上是一个整数链表)时所做的一些忍术内容,这可能会对你有所帮助。

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

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

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

您可以使用表达式树&将其修改为引入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;
           }
   }