我如何定义一个检查整数列表

How can i define a List of checked integers

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

考虑以下场景:

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

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

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

这是我的问题有什么替代方案吗?我可以定义一个检查整数列表吗?如何做一个列表,如果添加到列表的值超过限制,就会抛出异常?

更新:

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

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

假设 x 和 y 为正:

如果 (x + y) < x 则溢出

尝试引入IntWrapperclass,它负责将两个整数相加。

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

你在错误的层次上解决问题。首先,您的计算 - 它 returns 某种类型的值 - intlong 等。不应该检查溢出吗?不是溢出了,而是returnslong,比如

如果在添加到容器时仍然需要这样做,您可以像这样创建检查列表:

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);
    }
}

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

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

    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(string, int)

因此,如果由于 > int.MaxValue 或 < Int.MinValue 而失败,它将 return false,因此您可以相应地处理它。

希望对您有所帮助

基本思路

假设您想要这样的行为:

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 Whosebug
    }

    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 Whosebug
    }

    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 Whosebug
    }

    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 Whosebug
    }

    //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
}

现在,您拥有 CheckedInt 的真实 List



使用

简单地这样使用:

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 Whosebug
}

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 Whosebug
}

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

简单地说,您可以通过解析并将值转换为更大的类型来完成此操作,例如 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() 之前检查溢出。这样,您就可以更好地控制数据流。您可以忽略、记录、替换等溢出的数据。

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

2种处理方式:

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

Here is my question Is there any alternative for this? Can I define a list of checked integers? How can I make a list that throws an exception in the case where the value added to the list exceeds the limit?

溢出发生在传递给List之前的计算中,所以Listclass不可能检测到这种溢出。此处使用的是最严格意义上的溢出一词。

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

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

在其他答案中还有其他 "workarounds" 不完全符合您的要求,但这里是对为什么您不能按照您的要求做的基本解释:

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

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

编译器只是通过不强制您为每个 sub-expression 创建命名的临时变量来帮助您节省击键次数。因为那些临时变量必须被创建。

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