代码之家  ›  专栏  ›  技术社区  ›  AndiDog

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

  •  4
  • AndiDog  · 技术社区  · 14 年前

    我想检查一个简单的数学表达式是否会溢出(使用 checked catch(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,但不一定如此。)

    1 回复  |  直到 9 年前
        1
  •  7
  •   BenMorel Pankaj Kumar    10 年前

    NET中的浮点数不会像整数算术那样溢出。

    它们很有帮助地转到double.positiveifnity、double.negativenity或(特定于数学运算变成无效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定义编译时发出的指令(有些指令陷阱溢出,有些则不溢出)

    选中的块不影响在其外部定义的代码。例如,只使用函数:

    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上。没有任何操作 定义 在里面,这很重要。

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

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