有没有办法跟踪字典中项目的顺序?

Cor*_*ker 5 .net c# dictionary hashtable

我有一个Dictionary<Guid, ElementViewModel>.(ElementViewModel是我们自己的复杂类型.)我使用股票标准将项目添加到字典中items.Add(Guid.NewGuid, new ElementViewModel() { /*setters go here*/ });,

稍后我会删除部分或全部这些项目.

我的ElementViewModel的简单视图是这样的:

class ElementViewModel
{
    Guid Id { get; set; }
    string Name { get; set; }
    int SequenceNo { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

值得一提的是,如果发生移动和复制等其他操作,则在添加后,SequenceNos在集合中被压缩.{1,5,6} - > {1,2,3}

我的删除操作的简单视图是:

public void RemoveElementViewModel(IEnumerable<ElementViewModel> elementsToDelete)
{
    foreach (var elementViewModel in elementsToDelete)
        items.Remove(elementViewModel.Id);

    CompactSequenceNumbers();
}
Run Code Online (Sandbox Code Playgroud)

我将用一个例子说明问题:

我在字典中添加了3个项目:

var newGuid = Guid.NewGuid();
items.Add(newGuid, new MineLayoutElementViewModel { Id = newGuid, SequenceNo = 1, Name = "Element 1" });
newGuid = Guid.NewGuid();
items.Add(newGuid, new MineLayoutElementViewModel { Id = newGuid, SequenceNo = 2, Name = "Element 2" });
newGuid = Guid.NewGuid();
items.Add(newGuid, new MineLayoutElementViewModel { Id = newGuid, SequenceNo = 3, Name = "Element 3" });
Run Code Online (Sandbox Code Playgroud)

我删除了2个项目

RemoveElementViewModel(new List<ElementViewModel> { item2, item3 }); //imagine I had them cached somewhere.
Run Code Online (Sandbox Code Playgroud)

现在我想添加2个其他项目:

newGuid = Guid.NewGuid();
items.Add(newGuid, new MineLayoutElementViewModel { Id = newGuid, SequenceNo = 2, Name = "Element 2, Part 2" });
newGuid = Guid.NewGuid();
items.Add(newGuid, new MineLayoutElementViewModel { Id = newGuid, SequenceNo = 3, Name = "Element 3, Part 2" });
Run Code Online (Sandbox Code Playgroud)

关于此时字典的评估,我预计项目的顺序为"元素1","元素2,第2部分","元素3,第2部分"

但它实际上按以下顺序排列:"元素1","元素3,第2部分","元素2,第2部分"


我依靠这些项目的顺序是某种方式.为什么不如预期,我能做些什么呢?

SLa*_*aks 14

.Net词典在设计上是无序的.

你应该用一个KeyedCollection<TKey, TValue>代替; 它将保留项目添加到集合的顺序,并且还将使用哈希表进行快速查找.

例如:

class ElementViewModelCollection : KeyedCollection<Guid, ElementViewModel> {
    protected override Guid GetKeyForItem(ElementViewModel item) { return item.Id; }
}

items.Add(new MineLayoutElementViewModel { Id = Guid.NewGuid(), SequenceNo = 3, Name = "Element 3" });
Run Code Online (Sandbox Code Playgroud)

请注意,如果Id在将项添加到集合后更改属性,则需要ChangeItemKey在集合上调用该方法.我强烈建议您将该Id属性设置为只读.


Cor*_*ker 0

不幸的是,SortedDictionary 对于我们必须存储在其中的大量数据而言不够快,并且 KeyedCollection 达不到手动压缩元素的 SequenceNo 属性的目的。

严格来说,我们应该重写排序发生的方式,因为我的解决方案不是最漂亮的:

每次删除项目时,都会新建字典并将未删除的项目重新添加到新字典中,以保持默认顺序。--> 我承认这是可怕的做法。计划在压力减轻后立即更改。