You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改SortedDictionary自定义键属性后未重排?如何解决?

问题:SortedDictionary键修改后无法自动重新排序的解决办法

SortedDictionary设计为始终按键排序,但当自定义键对象的属性被修改后,字典不会自动感知键的变化并更新排序,导致遍历结果不符合预期。

示例代码

public class ContentKey
{
    public int key_num = 0;
}

public static void Main(string[] args)
{
    ContentKey a = new ContentKey() {key_num = 10};
    ContentKey b = new ContentKey() {key_num = 20};
    ContentKey c = new ContentKey() {key_num = 30};
    
    SortedDictionary<ContentKey, string> dict = new SortedDictionary<ContentKey, string>(new KeyComparer());
    dict.Add(a, "A");
    dict.Add(c, "C");
    dict.Add(b, "B");
    
    foreach(var key_value in dict)
    {
        Console.WriteLine("Key val: " + key_value.Key.key_num + " Str: " + key_value.Value);
    }

    a.key_num = 50;
    
    foreach(var key_value in dict)
    {
        Console.WriteLine("Key val: " + key_value.Key.key_num + " Str: " + key_value.Value);
    }
}
                                     
public class KeyComparer : IComparer<ContentKey>
{
    public int Compare(ContentKey a, ContentKey b)
    {
        return a.key_num.CompareTo(b.key_num);
    }
}

现象与需求

第一次遍历输出顺序为A(10)、B(20)、C(30),符合预期;但修改a的key_num为50后,第二次遍历仍保持原顺序,而非预期的B(20)、C(30)、A(50)。要求无需删除重新插入键(实际场景中无法追踪哪些键被修改)的前提下解决该问题。


解决方案1:将键设计为不可变类型(推荐)

从根源上避免键值变化的问题,把ContentKey设为不可变类型,让排序依据的属性只能在构造时赋值,后续无法修改。

修改后的ContentKey:

public class ContentKey
{
    public int KeyNum { get; } // 只读属性,仅构造时可赋值
    public ContentKey(int keyNum)
    {
        KeyNum = keyNum;
    }
}

使用方式:

ContentKey a = new ContentKey(10);
ContentKey b = new ContentKey(20);
ContentKey c = new ContentKey(30);

解决方案2:自定义可自动更新排序的字典包装类

通过监听键的属性变化事件,自动触发字典的排序更新。

步骤1:让键实现属性变更通知

public class ContentKey : INotifyPropertyChanged
{
    private int _keyNum;
    public int KeyNum
    {
        get => _keyNum;
        set
        {
            if (_keyNum != value)
            {
                _keyNum = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(KeyNum)));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

步骤2:实现自动排序的字典包装类

public class AutoSortedDictionary<TKey, TValue> where TKey : INotifyPropertyChanged
{
    private readonly SortedDictionary<TKey, TValue> _innerDict;
    private readonly IComparer<TKey> _comparer;

    public AutoSortedDictionary(IComparer<TKey> comparer)
    {
        _comparer = comparer;
        _innerDict = new SortedDictionary<TKey, TValue>(comparer);
    }

    public void Add(TKey key, TValue value)
    {
        key.PropertyChanged += Key_PropertyChanged;
        _innerDict.Add(key, value);
    }

    private void Key_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        var key = (TKey)sender;
        if (_innerDict.TryGetValue(key, out var value))
        {
            // 移除并重新添加键,触发排序更新
            _innerDict.Remove(key);
            _innerDict.Add(key, value);
        }
    }

    // 暴露遍历接口
    public IEnumerable<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return _innerDict;
    }

    // 按需实现其他字典方法,如ContainsKey、Remove、TryGetValue等
}

步骤3:使用自定义字典

var dict = new AutoSortedDictionary<ContentKey, string>(new KeyComparer());
dict.Add(a, "A");
dict.Add(c, "C");
dict.Add(b, "B");

修改a.KeyNum = 50后,字典会自动更新排序,遍历结果将符合预期。

解决方案3:手动触发全局重新排序

如果无法修改键类型,可在需要更新排序时,将所有键值对取出后重新构建字典:

// 在键属性修改后执行
var tempList = dict.ToList();
dict.Clear();
foreach (var kvp in tempList)
{
    dict.Add(kvp.Key, kvp.Value);
}

这种方法适合批量修改后统一更新排序的场景,但数据量较大时会有性能开销,需根据实际情况使用。


内容的提问来源于stack exchange,提问作者DragonGamer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 13:22:09