如何定义已检查整数的列表

时间:2016-01-12 13:48:03

标签: c# list exception integer-overflow

我有一个定义为List<int> myIntList = new List<int>();的整数列表像往常一样,我将使用myIntList.Add()方法为列表添加值。我面临的问题是列表中的值是动态的(某些计算结果),可能超过整数可以容纳的最大值。

考虑以下情况:

 int x = int.MaxValue;
 myIntList.Add(x + 1); 

这会将-2147483648添加到列表中,而不是抛出异常。我需要在这里抛出异常。我知道myIntList.Add(checked(x + 1));可以完美地完成这项工作,或者我甚至可以在checked{}中包含myIntList.Add(),如下所示:

 checked
     {
         myIntList.Add(12);
         myIntList.Add(int.MaxValue);
         myIntList.Add(x + 1);
     }

这是我的问题有什么替代方案吗?我可以定义已检查整数的列表吗?如何在列表中添加的值超出限制的情况下创建一个引发异常的列表?

更新

谢谢大家的回复,大多数人建议在将它们添加到列表之前检查整数(如果它超出边界则抛出异常)。这与我通过给定的片段checked{// add elements }完成的操作相同,它会在没有任何复杂条件检查的情况下抛出异常。

11 个答案:

答案 0 :(得分:8)

您正在错误的级别解决问题。首先,你的计算 - 它返回某种类型的值 - intlong等。不应该检查溢出吗?它没有溢出,但返回long,例如?

如果在添加到容器时仍然应该这样做,您可以创建如下的检查列表:

class CheckedList : List<int>
{
    public void Add(long x)
    {
        if (int.MaxValue < x || int.MinValue > x) throw new ArgumentOutOfRangeException("Invalid");
        var i = (int) x;
        base.Add(i);
    }
}

答案 1 :(得分:3)

基本理念

假设你想要这样的行为:

List<CheckedInt> myIntList = new List<CheckedInt>();    
CheckedInt check1 = int.MaxValue;
CheckedInt check2 = 1;
myIntList.Add(check1 + check2); //exception occurs!

最干净的方法之一(这样可以保留x + y等操作代码,但同时能够throwing exception)将是使用重载运算符 定义您自己的CheckedInt(基于int)。


实施

结构

CheckedInt struct将是这样的:

public struct CheckedInt {
    private int Value { get; set; }
    public CheckedInt(int value)
        : this() {
        Value = value;
    }

    public static implicit operator CheckedInt(int me) {
        return new CheckedInt(me);
    }

    public static CheckedInt operator +(CheckedInt lhs, CheckedInt rhs) {
        double testResult = (double)lhs.Value + (double)rhs.Value;
        if (testResult > int.MaxValue || testResult < int.MinValue)
            throw new MyCheckedIntException();
        return new CheckedInt(lhs.Value + rhs.Value); //note that direct lhs+rhs will cause StackOverflow
    }

    public static CheckedInt operator -(CheckedInt lhs, CheckedInt rhs) {
        double testResult = (double)lhs.Value - (double)rhs.Value;
        if (testResult > int.MaxValue || testResult < int.MinValue)
            throw new MyCheckedIntException();
        return new CheckedInt(lhs.Value - rhs.Value); //note that direct lhs-rhs will cause StackOverflow
    }

    public static CheckedInt operator *(CheckedInt lhs, CheckedInt rhs) {
        double testResult = (double)lhs.Value * (double)rhs.Value;
        if (testResult > int.MaxValue || testResult < int.MinValue)
            throw new MyCheckedIntException();
        return new CheckedInt(lhs.Value * rhs.Value); //note that direct lhs*rhs will cause StackOverflow
    }

    public static CheckedInt operator /(CheckedInt lhs, CheckedInt rhs) {
        double testResult = (double)lhs.Value / (double)rhs.Value;
        if (testResult > int.MaxValue || testResult < int.MinValue)
            throw new MyCheckedIntException();
        return new CheckedInt(lhs.Value / rhs.Value); //note that direct lhs-rhs will cause StackOverflow
    }

    //Add any other overload that you want

    public override string ToString() { //example
        return Value.ToString();
    }

    public bool Equals(CheckedInt otherInt) { //example
        return Value == otherInt.Value;
    }
}


例外

你也可以定义自己的例外。

public class MyCheckedIntException : Exception {
    public MyCheckedIntException() {
        //put something
}

public MyCheckedIntException(string message) : base(message) {
        //put something
}

    public MyCheckedIntException(string message, Exception inner) : base(message, inner) {
        //put something
}

现在,你有List CheckedInt个真实的人。


使用

只需像这样使用它:

CheckedInt check1 = int.MaxValue;
CheckedInt check2 = 1;

这句话:

List<CheckedInt> myIntList = new List<CheckedInt>();    
myIntList.Add(check1 + check2); //exception!

将为您抛出异常MyCheckedIntException


扩展,清洁外观

如果你想像其中任何一个一样使用它:

myIntList.Add(check1 + 1); //note that `1` is not type of checked integer
myIntList.Add(1 + check1); //note that `1` is not type of checked integer

然后只需将overloading添加到operator overloads

public static CheckedInt operator +(CheckedInt lhs, int rhs) { //note the type of rhs
    double testResult = (double)lhs.Value + (double)rhs;
    if (testResult > int.MaxValue || testResult < int.MinValue)
        throw new MyCheckedIntException();
    return new CheckedInt(lhs.Value + rhs); //note that direct lhs+rhs will cause StackOverflow
}

public static CheckedInt operator +(int lhs, CheckedInt rhs) { //not the type of lhs
    double testResult = (double)lhs + (double)rhs.Value;
    if (testResult > int.MaxValue || testResult < int.MinValue)
        throw new MyCheckedIntException();
    return new CheckedInt(lhs + rhs.Value); //note that direct lhs+rhs will cause StackOverflow
}

您也可以为所有其他运营商做同样的事情。

答案 2 :(得分:1)

您无法检查该总和的结果是否超出范围,因为如果您只有结果,则您没有所有必需的数据。如果你的问题确实存在溢出int,你有几个选择:

  1. 您可以为列表创建自己的类,例如@tenbits建议。
  2. 您可以为列表创建扩展方法 2a)创建与选项1中相同的Add方法。
    2b)创建方法,在其中添加数字并决定(你必须知道你想对这些数字做什么操作,但是将注释改为long等等应该没有任何问题):

    public static void Add(this List<int> list, int value, int otherValue)
    {
        if ((long)value + otherValue > int.MaxValue || 
            (long)value + otherValue < int.MinValue)
        {
            throw new ArgumentOutOfRangeException("Integer overflow");
        }
        else
        {
            list.Add(value + otherValue);
        }
    }
    
  3. 我认为您可以创建一些其他示例,但没有太大区别。

    然而,重要的是要注意,使用checked关键字(从我尝试过的)始终是最快的解决方案。实际上它几乎和没有检查的简单插入一样快,所以如果没有严重的理由不使用checked关键字,我必须推荐它。

答案 3 :(得分:1)

在添加之前,我会(ref):

  

Int.TryParse(string,int)

因此,如果因为&gt;而失败int.MaxValue或&lt; Int.MinValue,它将返回false,因此您可以相应地处理它。

希望这有帮助

答案 4 :(得分:1)

只需通过解析并将值转换为更大的类型(例如long:

)即可
List<int> myIntList = new List<int>();
int x = int.MaxValue;
myIntList.Add(int.Parse(((long)x + 1).ToString()));

它会抛出System.OverflowException。

myIntList.Add(int.Parse(((long)x - 1).ToString()));

否则将添加整数值。

答案 5 :(得分:1)

有一件事需要考虑。你在这里的实际意图是什么?我的意思是:如果您不想添加导致溢出的结果,为什么在实际尝试将它们添加到列表时检查它们?你怎么处理导致溢出的结果?你把它们添加到其他列表中吗?或者你忽略了它们?

我要做的是在实际调用List.Add()之前检查溢出。这样,您就可以更好地控制数据流。您可以忽略,记录,替换等溢出的数据。

只需要考虑一些事情。

答案 6 :(得分:1)

处理它的两种方法:

  1. 使用checked/unchecked包裹您的代码(正如您现在所做的那样)
  2. 使用/checked编译器选项(默认情况下已关闭)。

答案 7 :(得分:1)

  

这是我的问题这有什么替代方案吗?我可以定义一个   已检查整数列表?如何制作一个抛出的列表   添加到列表中的值超出的情况下的异常   限制?

在传递给List之前计算中发生溢出,因此List类不可能检测到这种溢出。溢出这个词在这里用得最严格。

替代方案基于您已经知道的内容,即使用checked上下文。您可以使用编译选项/checked,这可能会减轻您使用关键字的麻烦。请注意,调用代码(不是List代码)需要使用此选项进行编译。

答案 8 :(得分:1)

简短的回答是:不,你不能。

还有其他&#34;变通办法&#34;在其他答案中并没有完全符合您的要求,但这里有基本的解释,说明为什么您不能做您想做的事情:

编译时,代码基本上会分解为:

int x = int.MaxValue;
int temp = x + 1;
list.Add(temp);

编译器只是帮助您保存击键,而不是强制您为每个子表达式创建命名临时变量。因为必须创建这些临时变量。

要理解为什么{<1}}必须在调用x + 1方法之前计算,您需要了解CPU如何执行代码,一些基本程序集和一些编译概念。所有这些都超出了这个问题的范围 - 如果你想了解更多信息,可以提出一个新问题。

答案 9 :(得分:0)

尝试引入IntWrapper类,它负责添加两个整数。

public static class IntWrapper
{
  public static Int32 Add(this Int32 left, Int32 right)
  {
    if ((Int64)left + (Int64)right > (Int64)Int32.MaxValue)
      throw new ArgumentOutOfRangeException();
    return left + right;
  }
}

使用Add方法添加两个整数。

答案 10 :(得分:-3)

在存储到列表中之前,您需要检测计算结果中的溢出。

假设x和y是正数:

if(x + y)&lt; x然后溢出