2010-02-18 2 views
4

Я хочу проверить, будет ли простое математическое выражение переполняться (используя checked и catch(OverflowException)), но без необходимости использовать блок try-catch каждый раз. Таким образом, выражение (а не результат!) Должно быть передано функции checkOverflow, которая затем действует соответственно в случае переполнения.Лексический обзор в C# лямбда/анонимные делегаты

Это то, что я попытался, но не работает, поскольку не представляется лексическим охватом лямбда-выражений.

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, но необязательно.)

ответ

7

Номера с плавающей точкой в ​​.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 делать, просто написать, что это произошло?

Если это все, этот подход будет работать (я преобразовал в целое для вас)

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 // 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.ovf , Таким образом, два вызова к нему не могут изменить его, чтобы его проверить или нет после факта. Проверенный блок вокруг первого вызова в приведенном выше примере фактически имеет без эффекта на полученном ИЛ.Нет операций определяется внутри этого, что имеет значение для него.

Таким образом, ваша первоначальная идея заключалась в определении арифметической операции в одном месте (без проверки), а затем ее в другой точке (как вызов функции), но команда «checked» не может повлиять на код, который он не окружал во время компиляции ,

lambdas разрешает работу с деревьями выражений или анонимным делегатом (возможно, поддерживаются требуемыми синтетическими классами для хранения и поддержки любых связанных с закрытием переменных). В обоих случаях проверенный аспект любой части из них полностью определен там, где они определены, а не там, где они вызываются.

+0

Можете ли вы объяснить, почему переполнение не поймано при выполнении 'checked {exp(); } 'в блоке try * вместо * в лямбда? Когда оценивается 'a + b'? – AndiDog

+0

@ AndiDog, бит в моем редактировании не достаточно объясняет? Я постараюсь добавить к нему. – ShuggyCoUk

+0

Хорошо, теперь имеет смысл. благодаря – AndiDog

Смежные вопросы