.Net の浮動小数点数は、整数演算のようにオーバーフローしません。
それらは、Double.PositiveIfinity、Double.NegativeIfinity、または (数学演算が無効な Double.Nan になる場合に固有) に役立ちます。
これは、精度が大きく異なる 2 つの数値に直面した場合の浮動小数点の動作のために、やや複雑になることに注意してください。
Console.WriteLine(double.MaxValue);
Console.WriteLine(double.MaxValue * 2);
Console.WriteLine(double.MaxValue + 1);
Console.WriteLine(double.MaxValue + double.MaxValue);
与えます:
1.79769313486232E+308
Infinity
1.79769313486232E+308
Infinity
また、 checkOverflow 関数に何をさせたいのか明確ではありません。それが起こったと書いてください。
それだけの場合、このアプローチは機能します (私はあなたのために int に変換しました)
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!");
}
}
これが機能する理由を追加する必要
があります。checked は、変数に影響を与えるという意味でレキシカル スコープではありません。これはコンパイラによって解釈される領域であり、整数演算を行うこの内部のすべてのコードは、オーバーフロー トラップ命令を生成する必要があります。変数がどこから来るかは問題ではなく、どのコードがどこで定義されているかだけです。
あなたのメンタルモデルは次のようなものだと思います:
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
これはチェックがどのように機能するかではなく、チェックはコンパイル時に発行される命令を定義します (一部の命令はオーバーフローをトラップし、一部の命令はトラップしません)
チェックされたブロックは、その外側で定義されたコードには影響しません。たとえば、関数を使用するだけです:
int Times2(int a)
{
return a * 2;
}
void TheresNoDifferenceHere()
{
checked { Times2(int.MaxValue); }
Times2(int.MaxValue);
}
Times2 関数呼び出しは、次のように解決されます
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
もし使っていたら
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
mul と mul.ovf の使用の違いに注意してください。したがって、それを 2 回呼び出しても、後でチェックするかどうかを変更することはできません。上記の例の最初の呼び出しの周りのチェックされたブロックは、結果の IL に実際には影響しません。その内部で定義されている、重要な操作はありません。
したがって、元のアイデアは、ある場所で算術演算を定義し (チェックせずに)、別のポイントで実行することでした (関数呼び出しのように) が、「チェックされた」命令は、コンパイル時に囲まれなかったコードに影響を与えることはできません。
ラムダは、式ツリーまたは匿名デリゲートのいずれかに解決されます (おそらく、クロージャー関連の変数を保持および維持するために必要な合成クラスによってサポートされます)。どちらの場合も、それらの任意の部分のチェックされた側面は、それらが呼び出された場所ではなく、定義された場所で完全に定義されます。