自定义字典类排序时遇到未将对象引用到实例

问题遇到的现象

我想以多线程的办法去处理表格里的数据,并在其方法内部进行业务操作,于是我自定义了一个字典类MyDictionary。
但我想要给进行排序操作的时候,程序报错"未将对象引用到实例",总数量Count没有变化,但item都是对象的默认值。
我是漏掉了什么步骤了吗?

运行结果及报错内容

img

问题相关代码
public class MyDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable
    {
        /// <summary>
        /// 数据源
        /// </summary>
        private ConcurrentDictionary<TKey, TValue> _items;

        public MyDictionary()
        {
                _items = new ConcurrentDictionary<TKey, TValue>();
        }

        /// <summary>
        /// 获取或设置具有指定键的元素
        /// </summary>
        /// <param name="key">要获取或设置的元素的键</param>
        /// <returns></returns>
        public TValue this[TKey key]
        {
            get
            {
                return _items[key];
            }
            set
            {
                if (_items.ContainsKey(key))
                    _items[key] = value;
                else
                    Add(key, value);
            }
        }

        /// <summary>
        /// 获取或设置具有指定键的元素
        /// </summary>
        /// <param name="key">要获取或设置的元素的键</param>
        /// <returns></returns>
        public object this[object key] { get { return ((IDictionary)_items)[key]; } set { ((IDictionary)_items)[key] = value; } }

        /// <summary>
        /// 获得一个包含字典中的键的集合
        /// </summary>
        public ICollection<TKey> Keys { get { return _items.Keys; } }

        /// <summary>
        /// 获得一个包含字典中的键的集合
        /// </summary>
        ICollection IDictionary.Keys { get { return ((IDictionary)_items).Keys; } }

        /// <summary>
        /// 获得一个包含字典中的值的集合
        /// </summary>
        public ICollection<TValue> Values { get { return _items.Values; } }

        /// <summary>
        /// 获得一个包含字典中的值的集合
        /// </summary>
        ICollection IDictionary.Values { get { return ((IDictionary)_items).Values; } }

        /// <summary>
        /// 获取包含在字典中的键/值对的数目
        /// </summary>
        public int Count { get { return _items.Count(); } }

        /// <summary>
        /// 获取一个值,该值指示<see cref="ICollection"/>是否为只读
        /// </summary>
        public bool IsReadOnly { get { return false; } }

        private object _syncRoot;
        /// <summary>
        /// 获取可用于同步对<see cref="ArrayList"/>的访问的对象
        /// </summary>
        public object SyncRoot
        {
            get
            {
                if (_syncRoot == null)
                    Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
                return _syncRoot;
            }
        }

        /// <summary>
        /// 获取一个值,该值指示是否同步对<see cref="ArrayList"/>的访问(线程安全)
        /// </summary>
        public bool IsSynchronized { get { return true; } }

        /// <summary>
        /// 获取一个值,该值指示是否<see cref="ICollection"/>对象具有固定的大小
        /// </summary>
        public bool IsFixedSize { get { return false; } }        

        /// <summary>
        /// 将指定的键和值添加到字典中
        /// </summary>
        /// <param name="key">要添加的元素的键</param>
        /// <param name="value">要添加的元素的值。对于引用类型,该值可以为 null</param>
        public void Add(TKey key, TValue value)
        {
            if (!_items.TryAdd(key, value))
                throw new ArgumentException("字典中已存在具有相同键的元素");
        }

        /// <summary>
        /// 将指定的键和值添加到字典中
        /// </summary>
        /// <param name="item">要添加键/值对</param>
        public void Add(KeyValuePair<TKey, TValue> item)
        {
            Add(item.Key, item.Value);
        }

        /// <summary>
        /// 将指定的键和值添加到字典中
        /// </summary>
        /// <param name="key">要添加的元素的键</param>
        /// <param name="value">要添加的元素的值。对于引用类型,该值可以为 null</param>
        void IDictionary.Add(object key, object value)
        {
            TKey _key = ConvertKey(key);
            ((IDictionary)_items).Add(_key, (int)value);
        }

        /// <summary>
        /// 将所有元素从字典中移除
        /// </summary>
        public void Clear()
        {
            _items.Clear();
        }

        /// <summary>
        /// 确定是否字典中包含指定键
        /// </summary>
        /// <param name="key">定位的键</param>
        /// <returns></returns>
        public bool ContainsKey(TKey key)
        {
            return _items.ContainsKey(key);
        }

        /// <summary>
        /// 确定是否字典中包含指定键
        /// </summary>
        /// <param name="item">定位的键/值对</param>
        /// <returns></returns>
        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return _items.Contains<KeyValuePair<TKey, TValue>>(item);
        }

        /// <summary>
        /// 确定是否字典中包含指定键
        /// </summary>
        /// <param name="key">定位的键</param>
        /// <returns></returns>
        public bool Contains(object key)
        {
            TKey _key = ConvertKey(key);
            return _items.ContainsKey(_key);
        }

        /// <summary>
        ///  从第一个元素开始复制<see cref="Array"/>中的一系列元素,将它们粘贴到另一<see cref="Array"/>中(从第一个元素开始)
        /// </summary>
        /// <param name="array">接收数据的<see cref="Array"/></param>
        /// <param name="index">一个 32 位整数,它表示要复制的元素数目</param>
        public void CopyTo(Array array, int index)
        {
            if (index < 0)
                throw new ArgumentOutOfRangeException("index小于零");

            if (index > array.Length)
                throw new ArgumentException("index大于字典中的元素数");

            if (index > array.Length || array == null)
                throw new ArgumentNullException("未将对象引用到实例");

            Array.Copy(_items.ToArray(), array, index);
        }

        /// <summary>
        ///  从第一个元素开始复制<see cref="Array"/>中的一系列元素,将它们粘贴到另一<see cref="Array"/>中(从第一个元素开始)
        /// </summary>
        /// <param name="array">接收数据的<see cref="Array"/></param>
        /// <param name="arrayIndex">一个 32 位整数,它表示要复制的元素数目</param>
        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            Array.Copy(_items.ToArray(), array, arrayIndex);
        }

        /// <summary>
        /// 将带有指定键的值从字典中移除
        /// </summary>
        /// <param name="key">要移除的元素的键</param>
        /// <returns></returns>
        public bool Remove(TKey key)
        {
            TValue outValue = default(TValue);
            return _items.TryRemove(key, out outValue);
        }

        /// <summary>
        /// 将带有指定键/值对从字典中移除
        /// </summary>
        /// <param name="key">要移除的键/值对</param>
        /// <returns></returns>
        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            return Remove(item.Key);
        }

        /// <summary>
        /// 将带有指定键的值从字典中移除
        /// </summary>
        /// <param name="key">要移除的元素的键</param>
        /// <returns></returns>
        void IDictionary.Remove(object key)
        {
            TKey _key = ConvertKey(key);
            ((IDictionary)_items).Remove(_key);
        }

        /// <summary>
        /// 尝试从字典中获取与指定的键关联的值
        /// </summary>
        /// <param name="key">要获取的值的键</param>
        /// <param name="value">当此方法返回时,将包含字典中具有指定键的对象;如果操作失败,则包含类型的默认值</param>
        /// <returns></returns>
        public bool TryGetValue(TKey key, out TValue value)
        {
            return _items.TryGetValue(key, out value);
        }

        /// <summary>
        /// 返回一个循环访问集合的枚举器
        /// </summary>
        /// <returns></returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return new Enumerator(this, Enumerator.KeyValuePair);
        }

        /// <summary>
        /// 返回一个循环访问集合的枚举器
        /// </summary>
        /// <returns></returns>
        IDictionaryEnumerator IDictionary.GetEnumerator()
        {
            return new Enumerator(this, Enumerator.DictEntry);
        }

        /// <summary>
        /// 将<see cref="object"/>转换为<see cref="TKey"/>
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private TKey ConvertKey(object key)
        {
            TKey _key = key as TKey;
            if (key == null)
                throw new InvalidCastException("key不是有效类型");
            return _key;
        }

        Enumerator GetEnumerator()
        {
            return new Enumerator(this, Enumerator.KeyValuePair);
        }

        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
        {
            return new Enumerator(this, Enumerator.KeyValuePair);
        }

        private struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>, IDictionaryEnumerator
        {
            private Dictionary<TKey, TValue> dictionary;

            private KeyValuePair<TKey, TValue> current;

            private int index;

            private int getEnumeratorRetType;  // What should Enumerator.Current return?

            internal const int DictEntry = 1;
            internal const int KeyValuePair = 2;

            public Enumerator(MyDictionary<TKey, TValue> source, int _getEnumeratorRetType)
            {
                dictionary = new Dictionary<TKey, TValue>((IDictionary<TKey, TValue>)source._items);
                index = 0;
                getEnumeratorRetType = _getEnumeratorRetType;
                current = new KeyValuePair<TKey, TValue>();
            }

            public KeyValuePair<TKey, TValue> Current { get { return current; } }

            object IEnumerator.Current
            {
                get
                {
                    if (getEnumeratorRetType == DictEntry)
                        return new DictionaryEntry(current.Key, current.Value);
                    else
                        return new KeyValuePair<TKey, TValue>(current.Key, current.Value);
                }
            }

            object IDictionaryEnumerator.Key { get { return current.Key; } }

            object IDictionaryEnumerator.Value { get { return current.Value; } }

            DictionaryEntry IDictionaryEnumerator.Entry
            {
                get
                {
                    return new DictionaryEntry(current.Key, current.Value);
                }
            }

            public void Dispose()
            {

            }

            public bool MoveNext()
            {
                while (index < dictionary.Count)
                {
                    current = new KeyValuePair<TKey, TValue>(dictionary.ElementAt(index).Key, dictionary.ElementAt(index).Value);
                    index++;
                    return true;
                }

                index = dictionary.Count + 1;
                current = new KeyValuePair<TKey, TValue>();
                return false;
            }

            void IEnumerator.Reset()
            {
                index = 0;
                current = new KeyValuePair<TKey, TValue>();
            }
        }
    }

OrderByDescending()会执行到自定义字典类的CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)方法
旧代码里,我是直接调用了Array.Copy()。
利用Dnspy反编译了System.Core.dll内容,在Buffer(IEnumerable<TElement> source)内有这么一段
collection.CopyTo(array, 0);
第二个参数为0,自定义类的CopyTo第二个参数arrayIndex=>0。导致Array.Copy()既不报错,也没有复制数据。
按照微软给出的源码,我把CopyTo更改了

        /// <summary>
        ///  从第一个元素开始复制<see cref="Array"/>中的一系列元素,将它们粘贴到另一<see cref="Array"/>中(从第一个元素开始)
        /// </summary>
        /// <param name="array">接收数据的<see cref="Array"/></param>
        /// <param name="arrayIndex">一个 32 位整数,它表示要复制的元素数目</param>
        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            if (array == null)
                throw new ArgumentNullException("array不能为空");

            if (arrayIndex ! = 0)
                throw new ArgumentException("arrayIndex需要等于0");

            foreach (var item in _items)
            {
                array[arrayIndex++] = item;
            }
        }

不要企图给字典排序
字典是一组key,value键值对,它本身的顺序是不可改变的
在放代码之前,先说说你到底想干什么,写点伪代码出来
可能你的方向根本就不对,不要纠结语法这种问题

1.net本身就具备可排序字典 SortedDictionary
2. SortedDictionary本身非线程安全,故进行add,操作时候请lock他,这是对你的问题简单处理

现在用Dnspy追踪到item.ToArray()=>Array.Copy()。
无法从数组A复制到数组B,B数组自然而然为“空”数组,进而导致“未将对象引用到实例”。
这是什么原因?

更简单的方法也有,直接注释CopyTo(),在报错的接口选择“通过_items实现接口”。

void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
      ((ICollection<KeyValuePair<TKey, TValue>>)_items).CopyTo(array, arrayIndex);
}