C# lambda/匿名委托中的词法作用域

发布于 2024-08-22 04:50:18 字数 930 浏览 10 评论 0原文

我想检查一个简单的数学表达式是否会溢出(使用 checkedcatch(OverflowException)),但不需要每次都使用 try-catch 块。因此,应该将表达式(而不是结果!)传递给函数 checkOverflow,然后该函数会在发生溢出时采取相应的操作。

这是我尝试过的,但不起作用,因为 lambda 表达式似乎没有词法范围。

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's 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();

有什么解决办法吗? (使用 .NET 2,但不一定。)

I want to check whether a simple mathematical expression would overflow (using checked and catch(OverflowException)), but without the need to use a try-catch block every time. So the expression (not the result!) should be passed to a function checkOverflow, which then acts accordingly in case of an overflow.

This is what I attempted, but does not work as there doesn't seem to be lexical scoping for 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's 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 that? (Working with .NET 2, but not necessarily.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

土豪 2024-08-29 04:50:18

.Net 中的浮点数不会像整数运算那样溢出。

他们有助于转到 Double.PositiveIfinity、Double.NegativeIfinity 或(特定于数学运算无效的情况 Double.Nan)

注意,这也有些更复杂,因为面对两个精度截然不同的数字时的浮点行为。

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

这不是checked的工作方式,checked定义了编译时发出的指令(有些指令会捕获溢出,有些则不会)

checked块不会影响在其外部定义的代码。例如,仅使用函数:

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 的区别。
因此,对它的两次调用不能在事后更改它是否被检查。
上例中第一个调用周围的检查块实际上对生成的 IL 没有影响。它内部没有定义对其重要的操作。

因此,您最初的想法是在一个地方定义算术运算(不检查),然后在另一点运行它(就像函数调用一样),但“检查”指令不能影响它在编译时未包围的代码。

lambda 解析为表达式树或匿名委托(可能由所需的合成类支持来保存和维护任何与闭包相关的变量)。在这两种情况下,它们任何部分的检查方面都是在定义它们的地方完全定义的,而不是在调用它们的地方。

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 somewhat 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 function 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文