如何定义一个已选整数的列表?

3
我有一个整数列表,定义如下: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 } 所做的相同,它会在不进行任何复杂条件检查的情况下引发异常。


请您能否包含原因。 - sujith karivelil
这个 线程 可能会有用。 - KMB
@un-lucky 你不能直接使用 Int64 吗? - tchelidze
@KMB:我实际上正在使用的是 checked{} - sujith karivelil
1
但是在你尝试添加整数之前,List<>如何检查它的情况呢? - Patryk Spytkowski
简短的回答是你不能。请看我的回答以了解原因。 - AnorZaken
11个回答

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

3
基本思想:
假设您希望实现以下行为:
List<CheckedInt> myIntList = new List<CheckedInt>();    
CheckedInt check1 = int.MaxValue;
CheckedInt check2 = 1;
myIntList.Add(check1 + check2); //exception occurs!

将文本从英语翻译成中文:

最清晰的方法之一是这样做(使操作代码(例如x + y)可以保留,但同时能够抛出异常),即基于int 定义自己的CheckedInt(带有重载运算符)。



实现

结构体

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
}

现在,你拥有了一个真正的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
}

你可以同样地对所有其他运算符进行操作。

1

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

  1. You can create your own class for list, like @tenbits suggests.
  2. You can create extension method for your list.
    2a) Create the same Add method as in option 1.
    2b) Create method, which adds numbers in it and decides (you have to know what operation you want to do with those numbers, but there shouldn't be any issues with changing ints into longs and so on):

    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关键字,我必须推荐使用它。

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

否则将添加整数值。

请问您能否包含“相较于checked{//添加元素},这种方法的优势是什么?” - sujith karivelil

1
简短的回答是:不行。
虽然其他答案提供了一些“变通”方法,但它们并不能完全满足您的要求。以下是为什么您无法实现所需功能的基本解释:
当您编译代码时,它基本上会被分解成以下形式:
int x = int.MaxValue;
int temp = x + 1;
list.Add(temp);

编译器只是为了节省打字而不强制您为每个子表达式创建命名的临时变量。因为这些临时变量必须被创建。
要理解为什么在调用Add(...)方法之前必须计算x + 1,您需要了解CPU如何执行代码,一些基本的汇编语言和编译概念。所有这些都超出了此问题的范围 - 如果您想了解更多信息,请提出新问题。

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

使用 Int.TryParse(string, int)

因此,如果由于 > int.MaxValue 或 < Int.MinValue 而失败,它将返回 false,因此您可以相应地处理。
希望这有所帮助。

1
有一件事需要考虑。你的实际意图是什么?我的意思是:如果你不想添加导致溢出的结果,为什么在尝试将它们添加到列表时要检查它们?对于导致溢出的结果,你会怎么处理?你会将它们添加到其他列表吗?还是忽略它们?
我会在实际调用 List.Add() 之前检查是否会溢出。这样,您可以更好地控制数据流。您可以忽略、记录、替换等处理溢出的数据。
只是一些需要考虑的事情。

你为什么把这些问题发在答案里?最好发成评论。 - sujith karivelil

1

处理的两种方法:

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

1
这是我的问题,有没有其他替代方案?我能定义一个已检查整数列表吗?如何制作一个列表,在添加到列表的值超过限制时抛出异常?
溢出发生在传递给列表之前的计算中,因此列表类无法检测到这种溢出。这里的溢出一词是严格意义上的。
替代方案基于您已经知道的内容,即使用checked上下文。您可以使用编译选项/checked,这可能使您免于使用关键字。请注意,调用代码(而不是List代码)需要使用此选项进行编译。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接