“动态”;范围界定;C#Checked表达式的构造

“动态”;范围界定;C#Checked表达式的构造,c#,scope,checked,dynamic-scope,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(m

是否可能(在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
抛出
溢出异常

编辑:好吧,要么告诉我有没有办法,要么给我一个更好的办法(请)

我之所以需要它,是因为我有如下代码:

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)));
void do_op(int a,int b,针对mallints的操作,针对bigints的操作)
{
尝试
{
已检查(对于mallints(a,b));
}
捕获(溢出例外)
{
对于大整数((长)a,(长)b);
}
}
...
do_op(n1,n2,
(inta,intb)=>Console.WriteLine(“int:”+(a+b)),
(长a,长b)=>Console.WriteLine(“长:”+(a+b));

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

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

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

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

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

异常应该是异常,而不是通常的程序流。但现在我们不必担心:)

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

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

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();
}
公共静态int-SafeAdd(int-left,int-right)
{
如果(左==0 | |右==0 | |左<0和右>0 | |右<0和左>0)
//一个是0,或者它们都位于0的不同侧面
返回左+右;
else if(right>0&&left>0&&int.MaxValue-right>left)
//大于0且正常
返回左+右;
else if(right<0&&left<0&&int.MinValue-right
使用您自己的类型的示例:

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; }
}
公共结构MyNumber
{
公共MyNumber(int值){n=value;}
public int n;//值
公共静态MyNumber运算符+(MyNumber左,MyNumber右)
{
如果(左==0 | |右==0 | |左<0和右>0 | |右<0和左>0)
//一个是0,或者它们都位于0的不同侧面
返回新的MyNumber(left.n+right.n);//整数加法
else if(right>0&&left>0&&int.MaxValue-right>left)
//大于0且正常
返回新的MyNumber(left.n+right.n);//整数加法
else if(right<0&&left<0&&int.MinValue-right

如前所述,您将失去性能,但会获得异常。

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

应在实际发生操作的地方使用选中表达式或选中块

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

您可以使用表达式树并修改它来引入checkedformath操作符并执行它。此示例未经编译和测试,您将不得不对其进行进一步调整

   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;
           }
   }
void CheckedOp(int a,int b,表达式小,动作大){
var smallFunc=已检查(小);
试一试{
smallFunc(a,b);
}捕获(溢出例外oe){
大(a,b);
}
}
已选中操作(表达式exp)
{
var v=新的CheckedNodeVisitor();
var r=v.访问(实验体);
return((Expression)Expression.Lambda(r,r.Parameters)).Compile();
}
类CheckedNodeVisitor:ExpressionVisitor{
公开检查的nodevisitor(){
}
受保护的重写表达式访问二进制(BinaryExpression be){
开关(be.NodeType){
大小写表达式类型。添加:
返回表达式.AddChecked(be.Left,be.Right);
}
返回be;
}
}

似乎不可能,但我很难想象它的实际用途。当调用者必须控制溢出检查时,你的函数有问题。我认为这不是一个简单的方法。使用选中的上下文会导致编译器发出不同的IL指令(add.ovf vs add)。我尝试强制编译器内联me