Lexical overview in C # lambda / anonymous delegates

I want to check if a simple math expression will overflow (using checked and catch(OverflowException) ), but without having to use a try-catch block every time. Thus, the expression (and not the result!) Should be passed to the checkOverflow function, which then acts accordingly in case of overflow.

This is what I tried, but does not work, as it does not seem to be the lexical scope of lambda expressions.

 static void Main(string[] args) { double g, h; // Check if the expression g+h would overflow, *without* the need to use // try/catch around the expression // Both of the following attempts fail because there no lexical scoping checkOverflow((FloatReturningExpression) delegate() {return g+h;}); checkOverflow(() => g+h); Console.ReadKey(); } private static void checkOverflow(FloatReturningExpression exp) { try { checked { double f = exp(); } } catch(OverflowException) { Console.WriteLine("overflow!"); } } private delegate double FloatReturningExpression(); 

Is there any solution for this? (Work with .NET 2, but not required.)

+4
source share
1 answer

Bubble floating-point numbers in .Net do not overflow, since integer arithmetic can be performed.

They help go to Double.PositiveIfinity, Double.NegativeIfinity or (especially for cases when a mathematical operation becomes invalid Double.Nan)

note that this is also a bit more complicated due to the floating point behavior when encountering two numbers with very different accuracy.

 Console.WriteLine(double.MaxValue); Console.WriteLine(double.MaxValue * 2); Console.WriteLine(double.MaxValue + 1); Console.WriteLine(double.MaxValue + double.MaxValue); 

gives:

 1.79769313486232E+308 Infinity 1.79769313486232E+308 Infinity 

It's also unclear what you want your checkOverflow function to execute, just write what happened?

If all this works (I converted to int for you)

 void Main() { int a, b; a = int.MaxValue; b = 1; // Check if the expression a+b would overflow, *without* the need to use // try/catch around the expression checkOverflow(() => {checked { return a+b; }}); } private static void checkOverflow(Func<int> exp) { try { exp(); } catch(OverflowException) { Console.WriteLine("overflow!"); } } 

I have to add a reason why this works:
checked is not a lexical field in the sense of influencing variables. This is the area interpreted by the compiler, saying that all the code inside here that does integer arithmetic should generate overflow capture commands. no matter where the variables come from, only where exactly is the code defined.

I believe your mental model is something like:

 checked // enter a 'checked' state where all operations { // (say on the current thread) are checked code, function calls, etc. etc } // leave the checked mode, all operations are now unchecked 

This is not how it is checked, it works, it is checked, it determines which commands are issued at compile time (some commands intercept an overflow, and some do not)

A validated block does NOT affect code defined outside of it. For example, simply using functions:

 int Times2(int a) { return a * 2; } void TheresNoDifferenceHere() { checked { Times2(int.MaxValue); } Times2(int.MaxValue); } 

Times2 function call resolved with

 IL_0000: nop IL_0001: ldarg.1 IL_0002: ldc.i4.2 IL_0003: mul IL_0004: stloc.0 IL_0005: br.s IL_0007 IL_0007: ldloc.0 IL_0008: ret 

If you used

 int Times2(int a) { checked { return a * 2; } } IL_0000: nop IL_0001: nop IL_0002: ldarg.1 IL_0003: ldc.i4.2 IL_0004: mul.ovf IL_0005: stloc.0 IL_0006: br.s IL_0008 IL_0008: ldloc.0 IL_0009: ret 

Note the difference in using mul and mul.ovf. Thus, two appeals to him cannot change him for verification or not after the fact. The checked block around the first call in the above example does not actually affect the received IL. There are no specific operations that matter to him.

So your initial idea was to define an arithmetic operation in one place (without checking), and then doing it at another point (just like calling a function), but the โ€œcheckedโ€ command cannot affect code that it didn't surround at compile time.

lambdas allows work with expression trees or an anonymous delegate (it may be supported by the required synthetic classes for storing and supporting any closing variables). In both cases, the proven aspect of any part of them is fully defined where they are defined, and not where they are called.

+7
source

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


All Articles