Pertanyaan "Pelingkupan" Dinamis dari Ekspresi C # Diperiksa


Apakah mungkin (dalam C #) menyebabkan a checked(...) ekspresi untuk memiliki "lingkup" dinamis untuk pengecekan luapan? Dengan kata lain, dalam contoh berikut:

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
}

karena dalam ekspresi checked(add(max, 1)), Sebuah panggilan fungsi menyebabkan luapan, tidak OverflowException dilemparkan, meskipun ada limpahan selama dinamis sejauh mana checked(...) ekspresi.

Apakah ada cara untuk menyebabkan kedua cara untuk mengevaluasi int.MaxValue + 1 untuk melempar OverflowException?

EDIT: Baik, katakan padaku apakah ada cara, atau beri aku cara yang lebih baik untuk melakukan ini (tolong).

Alasan saya pikir saya perlu ini karena saya memiliki kode seperti:

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

Saya ingin ini dicetak int: ... jika a + b ada di int jangkauan, dan long: ... jika overflow bilangan bulat kecil. Apakah ada cara untuk melakukan ini yang lebih baik daripada hanya mengubah setiap single Action (yang saya punya banyak)?


4
2017-08-14 22:34


asal


Jawaban:


Singkatnya, tidak ada kemungkinan blok atau ekspresi yang dicentang memiliki lingkup dinamis. Jika Anda ingin menerapkan ini dalam keseluruhan basis kode Anda, Anda harus melihat untuk menambahkannya ke Anda opsi kompilator.

Memeriksa ekspresi atau blok yang diperiksa harus digunakan di tempat operasi benar-benar terjadi.

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

5
2017-08-20 14:01



Anda tidak harus menangkap pengecualian sebagai bagian dari aliran alami dari program Anda. Sebaliknya, Anda harus mengantisipasi masalah tersebut. Ada beberapa cara Anda dapat melakukan ini, tetapi dengan asumsi Anda hanya peduli int dan long dan ketika penambahan overflows:

EDIT: Menggunakan jenis yang Anda sebutkan di bawah ini dalam komentar Anda, bukan int dan long:

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

Ini membuat asumsi bahwa Anda memiliki konstruktor untuk RFBigInt yang mempromosikan a RFSmallInt. Ini harus sepele seperti BigInteger memiliki hal yang sama long. Ada juga cast eksplisit dari BigInteger untuk long yang dapat Anda gunakan untuk "menurunkan" nilai jika tidak melimpah.


4
2017-08-14 23:19



Pengecualian harus merupakan pengecualian, bukan alur program biasa. Tapi jangan pedulikan itu sekarang :)

Jawaban langsung atas pertanyaan Anda, saya percaya tidak, tetapi Anda selalu dapat mengatasi masalah itu sendiri. Saya memposting bagian kecil dari beberapa hal ninja yang saya buat ketika mengimplementasikan bilangan bulat tak terbatas (pada dasarnya daftar bilangan bulat yang terhubung) yang dapat membantu Anda.

Ini adalah pendekatan yang sangat sederhana untuk melakukan pemeriksaan tambahan secara manual jika kinerja tidak menjadi masalah. Cukup bagus jika Anda dapat membebani operator dari jenis, yaitu Anda mengontrol jenis.

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

Contoh dengan tipe Anda sendiri:

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

Seperti yang saya nyatakan sebelumnya, Anda akan kehilangan kinerja, tetapi dapatkan pengecualian.


1
2017-08-19 21:35



Anda bisa menggunakan Expression Tree & memodifikasinya untuk memperkenalkan operator Checked for math & mengeksekusinya. Sampel ini tidak dikompilasi dan diuji, Anda harus mengubahnya sedikit lebih banyak.

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

1
2017-08-25 05:45