2016-05-16 74 views
0

为什么要将值分配给List必须使用Add完成,但是可以使用[]运算符完成对数组的操作?将值分配给列表与数组以及ArgumentOutOfRangeException

例如:

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

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

不应该具有相同的行为呢?

+0

'不应该都具有相同的行为吗?不。为什么它应该是相同的?它们不是同一个东西... – Eser

+0

您需要先将字符串“asdf”添加到x,然后才能使用它。 x.add( “ASDF”)。现在列表是空的。 – Auguste

+0

为什么你会认为他们会有相同的行为?列表和数组是不同的东西。 – itsme86

回答

4

考虑看看的源代码清单(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++; 
     } 
    } 

注意,之前设置在列表内部的相应项目数组,它首先检查私人_size变量以确保它在范围内。但是,_size未设置为阵列的大小。大小在列表的各种添加/删除方法中增加/减少,因此即使您初始化一个初始容量为10的列表,即列表数组的容量为内部。下面是构造函数:

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

_size没有设置(并因此保持为0的初始值),除非您使用添加/删除/的AddRange /等。或者使用接受IEnumerable的构造函数(在这种情况下,因为IEnumerable中的项数)。

如果你仔细想想,这是有道理的。列表的想法是,您不必担心数字索引的复杂性(和丑陋),并在容量需要更改时调整/复制数组。在实例化List之后,内部数组的大小应该与开发人员无关。如果您想微观管理内部数组的使用方式,那么您应该创建自己的实现,或者只使用一个数组。

-2

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

更新: 这个重载可以帮助你,当你处理大集合,你几乎可以确定你会把约N个项目放入你的列表中。因此,您可以在创建时保留内存,并避免在向此列表中添加项目时进行内存分配(有时可能会很慢)。

+0

尽管如此,这并不能真正解决问题。问题是为什么,如果一个列表是以特定的容量创建的,那么这个容量就不能被访问。初始化容量为10的数组实际上并没有为数组添加任何内容,但是在创建数组后,您仍然可以明确地指定每个元素。 – DVK

1

列表的内部结构与array不同。在array中,您的定义中包含大小的项目,因此需要使这些对象在内存中由CLR实时存储。

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

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

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

的情况下,你不必对list<T>/array1指数,在array,你得到的default(T)(考虑T为你的类型),并在列表中你会得到一个Exception

+0

我喜欢这样想的方式是当你初始化一个数组时你有一组方框。当你用一个size参数初始化一个列表时,你有一个可以容纳一组框的仓库(但是在你添加()它们之前没有框) –

+0

谢谢@Steve,你是对的。我已经更新了我的答案。 –

+0

声明“列表的内部结构与数组不同”,“但它不会在内存中实现”,这些陈述并不准确。一个列表,内部是一个数组。 List类只是它的一个包装器,它处理调整大小,复制等。 – DVK

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";