排序列表<>和它奇怪的二传手

时间:2015-01-20 01:15:46

标签: c# .net setter sortedlist

我最初有一些代码,在简化后,看起来像这样:

var planets = new List<Planet>
{
    new Planet {Id = 1, Name = "Mercury"},
    new Planet {Id = 2, Name = "Venus"},
};

我进入了一个列表正在一次填充的场景,但读取速度不够快。因此,我将其更改为使用SortedList。

我后来意识到我可以像这样重写它

var planets = new SortedList<int, Planet>
{
    {1, new Planet {Id = 1, Name = "Mercury"}},
    {2, new Planet {Id = 2, Name = "Venus"}},
    //in my actual code, i am reading the ids from a db
};

但在我采用这种方法之前,我的代码就像这样写了

var planets = new SortedList<int, Planet>
{
    Keys = {1, 2},
    Values =
    {
        new Planet {Id = 1, Name = "Mercury"},
        new Planet {Id = 2, Name = "Venus"},
    }
};

给了我这个例外

System.NotSupportedException: This operation is not supported on SortedList
nested types because they require modifying the original SortedList.
  at System.ThrowHelper.ThrowNotSupportedException(ExceptionResource resource)
  at System.Collections.Generic.SortedList`2.KeyList.Add(TKey key)

我觉得很奇怪,因为恕我直言,我并没有真正修改原来的SortedList&#34;正如它声称的那样,以及#34;嵌套类型&#34;它在说什么?它是SortedList内部的键列表吗?

我看到Keys中的ValuesSortedList属性实际上并没有设置者。它们是只读属性,但是,我没有得到编译时错误。我可以进行一次固定调用,我可以在堆栈跟踪中看到KeyList.Add。我觉得失败的唯一原因是因为SortedList内的明确检查,这对我来说似乎很奇怪!

例如 var str = new String {Length = 0};按预期给出了编译时错误,因为Length是只读属性,planets.Keys = null;

也是如此

有人请告诉我 - 我在这里看到了什么简单的事实?

2 个答案:

答案 0 :(得分:3)

您编写的代码与此类似:

var planets = new SortedList<int, Planet>();
planets.Keys.Add(1);
planets.Keys.Add(2);
planets.Values.Add(new Planet { Id = 1, Name = "Mercury" });
planets.Values.Add(new Planet { Id = 2, Name = "Venus" });

SortedList要求您通过SortedList<TKey, TValue>.Add(TKey key, TValue value)方法同时添加值和键,以便它可以按键对值进行排序。内部用于IList<T>Keys的{​​{1}}的实施不支持通过Values方法单独添加相应的键或值。

您应该可以通过调用IList<T>.Add(T value)Keys.Add(...)

来重现此错误

答案 1 :(得分:0)

我对SortedList的初步查询现已最小化了对阵列,集合和放大器的关注。对象初始值设定项,以及编译器以不同方式解释它们的方式。再次感谢@Haney第一个引导我走向这一观点的答案,并感谢ILSpy了解这些见解。

以下是一些数组和集合初始值设定项:

int[] a = { 1, 2, 3 };
int[] b = new int[] { 1, 2, 3 };
IList<int> c = { 1, 2, 3 };
IList<int> d = new int[] { 1, 2, 3 };

他们看起来都很相似。在这里,编译器为&amp;产生完全相同的输出。湾对于c,我们将得到这个编译时错误:

  

只能使用数组初始值设定项表达式来分配数组类型。   请尝试使用新表达式。

这是有道理的,因为我们不应该为集合使用数组初始值设定项。但是,然后,d产生的结果与&amp;湾我认为这也是一个数组初始化器。显然不是。

现在考虑这个课程

class MyCollectionContainer
{
    public int[] MyIntArray { get; set; }
    public IList<int> MyList { get; set; }
}

以及在其上运行的代码

var containerA = new MyCollectionContainer { MyIntArray = { 1, 2, 3 } };
var containerB = new MyCollectionContainer { MyIntArray = new int[]{ 1, 2, 3 } };
var containerC = new MyCollectionContainer { MyList = { 1, 2, 3 } };
var containerD = new MyCollectionContainer { MyList = new int[]{ 1, 2, 3 } };

containerA给出了这个编译时错误:

  

无法初始化类型&#39; int []&#39;使用集合初始值设定项

对于containerB,编译器有效地将其转换为此代码:

MyCollectionContainer myCollectionContainer = new MyCollectionContainer();
myCollectionContainer.MyIntArray = new int[] {1, 2, 3};

对于containerD,它几乎是一样的,除非它的另一个属性被初始化:

MyCollectionContainer myCollectionContainer = new MyCollectionContainer();
myCollectionContainer.MyList = new int[] {1, 2, 3};

对于containerC,编译器将其变形为:

MyCollectionContainer myCollectionContainer = new MyCollectionContainer();
myCollectionContainer.MyList.Add(1);
myCollectionContainer.MyList.Add(2);
myCollectionContainer.MyList.Add(3);

由于NullReferenceException未初始化,因此会产生运行时MyList

这意味着在这里初始化集合容器对象的唯一有效方法是containerB和containerD。对我来说,这清楚地表明,与阵列和数据相比,对象初始化器是不同的。集合初始值设定项,以编译器解释它们的方式。