Well floating point numbers in .Net don't overflow in the way integer arithmetic can.
They helpfully go to Double.PositiveIfinity, Double.NegativeIfinity or (specific to in cases where the mathematical operation becomes invalid Double.Nan)
note that this is also somehwat more complex because of the floating point behaviour when faced with two numbers with very different precision.
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
Also it is not clear what you want your checkOverflow funtion to do, just write that it happened?
If that's all this approach will work (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 should add the reason why this works:
checked is not a lexical scope in the sense of affecting variables. It is a region interpreted by the compiler as saying, all code inside here which does integer arithmetic should generate the overflow trapping instructions. it doesn't matter where the variables come from, only what code is defined where.
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 checked works, checked defines what instructions are emitted at compile time (some instructions trap overflow, some do not)
The checked block does NOT affect code defined outside of it. For example just using functions:
int Times2(int a)
{
return a * 2;
}
void TheresNoDifferenceHere()
{
checked { Times2(int.MaxValue); }
Times2(int.MaxValue);
}
The Times2 function call resolves down to something like
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 had 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 the two calls to it cannot change it to be checked or not after the fact.
The checked block around the first call in the example above actually has no effect on the resulting IL. There are no operations defined inside it that matter to it.
Thus your original idea was defining the arithmetic operation in one place (without checking) then running it at another point (just like a function call) but the 'checked' instruction cannot affect code that it did not surround at compile time.
lambdas resolve down to either expression trees or an anonymous delegate (perhaps backed by the required synthetic classes to hold and maintain any closure related variables). In both cases the checked aspect of any part of them is entirely defined where they are defined, not where they are called.