Dynamic "Scoping" of a validated C # expression

Is it possible (in C #) to force the checked(...) expression to have a dynamic "scope" to check for overflow? In other words, in the following example:

 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 } 

because in the checked(add(max, 1)) expression checked(add(max, 1)) function call causes an overflow, no OverflowException thrown, although the dynamic length of the checked(...) expression overflows.

Is there a way to get both ways to evaluate int.MaxValue + 1 to throw an OverflowException ?

EDIT: Well, tell me if there is a way, or give me a better way to do this (please).

I think I need this because I have code like:

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

I want this to print int: ... if a + b is in the range of int and long: ... if the addition overflows with a small integer. Is there a way to do this better than just changing every Action (of which I have a lot)?

+4
source share
4 answers

To be short, it is not that the checked blocks or expressions cannot have a dynamic region. If you want to apply this in your entire code base, you should add it to the compiler options .

Validated expressions or marked blocks should be used where the operation actually takes place.

  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
source

You should not catch exceptions as part of the natural flow of your program. Instead, you should anticipate the problem. There are several ways to do this, but under the condition that you just care about int and long , and when the addition overflows:

EDIT: using the types listed below in the comment instead of int and long :

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

This makes the assumption that you have a constructor for RFBigInt that promotes RFSmallInt . This should be trivial as BigInteger has the same for long . There is also an explicit expression from BigInteger to long that you can use to "lower" the value if it is not full.

+4
source

An exception should be an exception, not a regular program flow. But this time it does not bother :)

The direct answer to your question, I think not, but you can always solve the problem yourself. I am posting a small portion of some of the ninja materials that I made while implementing unlimited integers (essentially a linked list of integers) that could help you.

This is a very simplified approach for performing a test add manually if performance is not a problem. It is very nice if you can overload type operators, i.e. You control types.

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

An example with your types:

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

As I said earlier, you will lose performance but get exceptions.

+1
source

You can use the Expression Tree and modify it to enter Tested for the math operator and execute it. This sample is not compiled or tested, you will have to configure it a bit.

  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
source

Source: https://habr.com/ru/post/1497131/


All Articles