将值分配给列表与数组和ArgumentOutOfRangeException

时间:2016-05-16 20:01:46

标签: c# arrays collections syntax

为什么必须使用ListAdd分配值,但是对于数组,可以使用[]运算符完成?

例如:

    string[] y = new string[10];
    y[0] = "asdf"; //fine

    List<string> x = new List<string>(10);
    x[0] = "asdf"; //ArgumentOutOfRangeException 

两者都不应该有相同的行为吗?

4 个答案:

答案 0 :(得分:4)

看一下List(of T)的源代码,你会看到索引属性getter / setter看起来像这样:

    // Sets or Gets the element at the given index.
    //
    public T this[int index] {
        get { 
            // Fllowing trick can reduce the range check by one
            if ((uint) index >= (uint)_size) { 
                ThrowHelper.ThrowArgumentOutOfRangeException(); 
            }
            return _items[index]; 
        }
        set {
            if ((uint) index >= (uint)_size) {
                ThrowHelper.ThrowArgumentOutOfRangeException(); 
            }
            _items[index] = value; 
            _version++; 
        }
    } 

请注意,在List的内部数组中设置相应的项之前,它首先检查私有_size变量以确保它在范围内。但是,_size未设置为数组的大小。在List的各种添加/删除方法中,大小会递增/递减,因此即使您实例化初始容量为10的列表,也就是List的数组的内部容量。这是构造函数:

    // Constructs a List with a given initial capacity. The list is
    // initially empty, but will have room for the given number of elements 
    // before any reallocations are required. 
    //
    public List(int capacity) { 
        if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
        _items = new T[capacity];
    }

除非您使用Add / Remove / AddRange / etc,否则_size未设置(因此保持为其初始值0)。或者使用接受IEnumerable的构造函数(在这种情况下,大小因为IEnumerable中的项目数)。

如果你考虑它就有意义了。列表的概念是这样的,当容量需要改变时,您不必担心数字索引的复杂性(和丑陋)以及调整大小/复制数组的大小。在实例化List之后,内部数组的大小应该与开发人员无关。如果你想微观管理内部数组的使用方式,那么你应该创建自己的实现,或者只使用数组。

答案 1 :(得分:1)

列表的内部结构与array不同。在array中,您在其定义中定义了大小的项目,而具有这些对象所需的内存大小由CLR在内存中实现。

list<T>中,您可以定义列表中的项目的最大值。那是( 的一部分)你必须调用Add方法在list<T>中添加对象的原因。您可以像在构造函数上一样为列表定义初始Capacity。如果您需要添加超过容量,列表将为您重新排列。该框架为您管理列表中有多少项目。

另一个重要的事情是,在这两种情况下,您都可以通过index进行访问。样本:

var obj = list[1];
var obj2 = array[1];

如果您在list<T> / array上没有1索引,则在array中,您会获得default(T)(正在考虑T作为您的类型),在列表中,您将获得Exception

答案 2 :(得分:1)

您实现阵列的方式是正确的。

创建数组时需要声明数组的大小。没有办法解决这个问题。

但是,列表的大小更灵活。您可以根据需要添加任意数量的元素,而无需声明初始大小。但是,在添加元素后,您可以通过索引号访问或编辑它们。这是一个例子。

您正在获取该异常,因为从技术上讲,在您实际向其添加值之前,列表不会填充索引。如果能清除它,请告诉我。

    //You can add your elements when you instantiate it
    List<string> names = new List<string>{"Alex", "Tommy", "Bob"};

    //Or you can add them later
    List<string> cities = new List<string>();

    cities.Add("Denver");
    cities.Add("New York");

    //Now that they are created you can access or edit any of the elements within them.
    names[2] = "Gerard";
    cities[1] = "San Francisco";

答案 3 :(得分:-2)

此构造函数不会在列表中创建任何元素。它只为可以添加到此列表的项目保留内存。 您仍然必须手动将项目插入列表,然后才能以这种方式使用。

UPD: 当您处理大型集合时,这种重载可以帮助您,并且您几乎可以确定您将N个项目放入列表中。因此,您可以在创建时保留内存,并在将项添加到此列表时避免内存分配(有时可能会很慢)。