简体   繁体   English

C#lambda /匿名代表中的词法范围

[英]Lexical scoping in C# lambda/anonymous delegates

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. 我想检查一个简单的数学表达式是否会溢出(使用checkedcatch(OverflowException) ),但不需要每次都使用try-catch块。 So the expression (not the result!) should be passed to a function checkOverflow , which then acts accordingly in case of an overflow. 因此表达式(不是结果!)应该传递给函数checkOverflow ,然后在溢出的情况下相应地执行。

This is what I attempted, but does not work as there doesn't seem to be lexical scoping for lambda expressions. 这是我尝试过的,但不起作用,因为似乎没有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();

Is there any solution for that? 那有什么解决方案吗? (Working with .NET 2, but not necessarily.) (使用.NET 2,但不一定。)

Well floating point numbers in .Net don't overflow in the way integer arithmetic can. .Net中的浮点数不会以整数运算的方式溢出。

They helpfully go to Double.PositiveIfinity, Double.NegativeIfinity or (specific to in cases where the mathematical operation becomes invalid Double.Nan) 他们有帮助地转到Double.PositiveIfinity,Double.NegativeIfinity或(特定于数学运算变为无效的情况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? 还不清楚你想要checkOverflow函数做什么,只是说它发生了吗?

If that's all this approach will work (I converted to int for you) 如果这是所有这种方法都可行(我为你转换为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!");
    }
}

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

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. 注意使用mul和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. 上面示例中第一次调用周围的已检查块实际上对生成的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. 因此,您最初的想法是在一个地方定义算术运算(不检查),然后在另一个点运行它(就像函数调用一样),但'checked'指令不会影响它在编译时没有包围的代码。

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). lambdas解析为表达式树或匿名委托(可能由所需的合成类支持,以保存和维护任何与闭包相关的变量)。 In both cases the checked aspect of any part of them is entirely defined where they are defined, not where they are called. 在这两种情况下,它们的任何部分的检查方面都是在定义它们的地方完全定义的,而不是在它们被调用的地方。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM