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

本文关键字:检查 整数 列表 何定义 定义 | 更新日期: 2023-09-27 18:30:02

我有一个定义为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 }中所做的相同——它将在没有任何复杂条件检查的情况下抛出异常。

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

您在错误的层面上解决了问题。首先,你的计算-它返回某种类型的值-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);
    }
}

基本理念

假设你想要这样的行为:

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
}

现在,你有一个真正的ListCheckedInt



使用

只需这样使用:

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
}

您可以对所有其他操作符执行同样的操作。

您不能检查该和的结果是否溢出范围,因为如果只有结果,则没有所有所需的数据。如果你的问题真的是int溢出,你有几个选择:

  1. 您可以像@tenbits建议的那样,为列表创建自己的类
  2. 您可以为列表创建扩展方法
    2a)创建与选项1中相同的Add方法。
    2b)创建方法,在其中添加数字并决定(你必须知道你想对这些数字进行什么操作,但将int改为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);
        }
    }
    

我认为你可以创造一些其他的例子,但没有太大的区别。

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

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

Int.TryParse(字符串,Int)

因此,如果由于大于int.MaxValue或<Int.MinValue,它将返回false,因此您可以相应地处理它。

希望这能帮助

只需解析并将值强制转换为更大的类型,如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()));

否则将添加整数值。

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

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

只是一些需要考虑的事情。

两种处理方法:

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

这是我的问题。有其他选择吗?我可以定义检查的整数列表?我怎样才能列出一个抛出添加到列表中的值超过限度

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

另一种选择是基于您已经知道的内容,即使用checked上下文。您可以使用compilation选项/checked,这可能会使您不用使用关键字。请注意,调用代码(而不是List代码)需要使用此选项进行编译。

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

还有其他"变通办法"在其他答案中并不能完全达到你想要的效果,但以下是你为什么不能达到你想要目的的基本解释:

当你编译代码时,你的代码基本上会分解成这样:

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

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

要理解为什么在调用Add(...)方法之前必须计算x + 1,您需要了解CPU是如何执行代码、一些基本汇编和一些编译概念的。所有这些都超出了这个问题的范围-如果你想了解更多,请问一个新问题。

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

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方法将两个整数相加。

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

假设x和y为正:

如果(x+y)<x然后溢出