是否有可能(在C#中)使checked(...)
表达式具有动态“范围”以进行溢出检查?换句话说,在以下示例中:
int add(int a, int b)
{
return a + b;
}
void test()
{
int max = int.MaxValue;
int with_call = checked(add(max, 1)); // does NOT cause OverflowException
int without_call = checked(max + 1); // DOES cause OverflowException
}
因为在表达式checked(add(max, 1))
中,函数调用会导致溢出,即使在动态期间出现溢出,也不会抛出OverflowException
checked(...)
表达式的范围。
有没有办法让两种方式评估int.MaxValue + 1
投掷OverflowException
?
我认为我需要这个的原因是因为我的代码如下:
void do_op(int a, int b, Action<int, int> forSmallInts, Action<long, long> forBigInts)
{
try
{
checked(forSmallInts(a, b));
}
catch (OverflowException)
{
forBigInts((long)a, (long)b);
}
}
...
do_op(n1, n2,
(int a, int b) => Console.WriteLine("int: " + (a + b)),
(long a, long b) => Console.WriteLine("long: " + (a + b)));
如果int: ...
在a + b
范围内,我希望打印int
,如果小整数添加溢出,我希望long: ...
。有没有办法做到这一点,而不仅仅是改变每一个Action
(我有很多)?
答案 0 :(得分:5)
简而言之,检查块或表达式不可能具有动态范围。 如果您想在整个代码库中应用此功能,则应将其添加到compiler options。
在实际发生操作的地方应使用经过检查的表达式或已检查的块。
int add(int a, int b)
{
int returnValue = 0;
try
{
returnValue = checked(a + b);
}
catch(System.OverflowException ex)
{
//TODO: Do something with exception or rethrow
}
return returnValue;
}
void test()
{
int max = int.MaxValue;
int with_call = add(max, 1);
}
答案 1 :(得分:4)
作为程序自然流程的一部分,您不应该捕获异常。相反,你应该预见到这个问题。您可以通过多种方式执行此操作,但假设您只关心int
和long
以及何时添加溢出:
编辑:在评论中使用您在下面提到的类型,而不是int
和long
:
void Add(RFSmallInt a, RFSmallInt b)
{
RFBigInt result = new RFBigInt(a) + new RFBigInt(b);
Console.WriteLine(
(result > RFSmallInt.MaxValue ? "RFBigInt: " : "RFSmallInt: ") + result);
}
这假设你有RFBigInt
的构造函数来提升RFSmallInt
。这应该是微不足道的,因为BigInteger
对long
具有相同的效果。还有一个从BigInteger
到long
的显式转换,如果值不溢出,您可以使用它来“降级”。
答案 2 :(得分:1)
异常应该是异常,而不是通常的程序流程。但是暂时不要关心它。)
直接回答你的问题我认为不是,但你可以随时解决问题。我发布了我在实现无界整数(实际上是一个整数的链表)时所做的一些忍者的一小部分,这可能对你有所帮助。
如果性能不是问题,这是一种非常简单的手动检查添加方法。如果您可以重载类型的运算符,即控制类型,那就太好了。
public static int SafeAdd(int left, int right)
{
if (left == 0 || right == 0 || left < 0 && right > 0 || right < 0 && left > 0)
// One is 0 or they are both on different sides of 0
return left + right;
else if (right > 0 && left > 0 && int.MaxValue - right > left)
// More than 0 and ok
return left + right;
else if (right < 0 && left < 0 && int.MinValue - right < left)
// Less than 0 and ok
return left + right;
else
throw new OverflowException();
}
您自己的类型示例:
public struct MyNumber
{
public MyNumber(int value) { n = value; }
public int n; // the value
public static MyNumber operator +(MyNumber left, MyNumber right)
{
if (left == 0 || right == 0 || left < 0 && right > 0 || right < 0 && left > 0)
// One is 0 or they are both on different sides of 0
return new MyNumber(left.n + right.n); // int addition
else if (right > 0 && left > 0 && int.MaxValue - right > left)
// More than 0 and ok
return new MyNumber(left.n + right.n); // int addition
else if (right < 0 && left < 0 && int.MinValue - right < left)
// Less than 0 and ok
return new MyNumber(left.n + right.n); // int addition
else
throw new OverflowException();
}
// I'm lazy, you should define your own comparisons really
public static implicit operator int(MyNumber number) { return number.n; }
}
正如我之前所说,你将失去表现,但获得例外。
答案 3 :(得分:1)
你可以使用Expression Tree&amp;修改它以介绍Checked for math operator&amp;执行它。此示例未经过编译和测试,您将不得不进行更多调整。
void CheckedOp (int a, int b, Expression <Action <int, int>> small, Action <int, int> big){
var smallFunc = InjectChecked (small);
try{
smallFunc(a, b);
}catch (OverflowException oe){
big(a,b);
}
}
Action<int, int> InjectChecked( Expression<Action<int, int>> exp )
{
var v = new CheckedNodeVisitor() ;
var r = v.Visit ( exp.Body);
return ((Expression<Action<int, int>> exp) Expression.Lambda (r, r. Parameters) ). Compile() ;
}
class CheckedNodeVisitor : ExpressionVisitor {
public CheckedNodeVisitor() {
}
protected override Expression VisitBinary( BinaryExpression be ) {
switch(be.NodeType){
case ExpressionType.Add:
return Expression.AddChecked( be.Left, be.Right);
}
return be;
}
}