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 + bは、これを出力します。単純にすべてを変更するよりも優れたこれを行う方法はありますか (私はたくさん持っています)?intlong: ...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

プログラムの自然な流れの一部として例外をキャッチするべきではありません。代わりに、問題を予測する必要があります。これを行う方法はいくつかありますが、足し算がいつオーバーフローするかだけを気にすると仮定する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);   
}

RFBigIntこれは、をプロモートするためのコンストラクターがあることを前提としていますRFSmallIntBigIntegerこれは、 と同じように簡単なはずです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

Expression Tree を使用して変更し、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 に答える