字典查找抛出“索引在数组边界之外”
本文关键字:边界 数组 索引 查找 字典 | 更新日期: 2023-09-27 18:12:00
我收到一个错误报告,似乎来自以下代码:
public class AnimationChannelCollection : ReadOnlyCollection<BoneKeyFrameCollection>
{
private Dictionary<string, BoneKeyFrameCollection> dict =
new Dictionary<string, BoneKeyFrameCollection>();
private ReadOnlyCollection<string> affectedBones;
// This immutable data structure should not be created by the library user
internal AnimationChannelCollection(IList<BoneKeyFrameCollection> channels)
: base(channels)
{
// Find the affected bones
List<string> affected = new List<string>();
foreach (BoneKeyFrameCollection frames in channels)
{
dict.Add(frames.BoneName, frames);
affected.Add(frames.BoneName);
}
affectedBones = new ReadOnlyCollection<string>(affected);
}
public BoneKeyFrameCollection this[string boneName]
{
get { return dict[boneName]; }
}
}
这是读取字典的调用代码:
public override Matrix GetCurrentBoneTransform(BonePose pose)
{
BoneKeyFrameCollection channel = base.AnimationInfo.AnimationChannels[pose.Name];
}
这是创建字典的代码,在启动时发生:
// Reads in processed animation info written in the pipeline
internal sealed class AnimationReader : ContentTypeReader<AnimationInfoCollection>
{
/// <summary>
/// Reads in an XNB stream and converts it to a ModelInfo object
/// </summary>
/// <param name="input">The stream from which the data will be read</param>
/// <param name="existingInstance">Not used</param>
/// <returns>The unserialized ModelAnimationCollection object</returns>
protected override AnimationInfoCollection Read(ContentReader input, AnimationInfoCollection existingInstance)
{
AnimationInfoCollection dict = new AnimationInfoCollection();
int numAnimations = input.ReadInt32();
/* abbreviated */
AnimationInfo anim = new AnimationInfo(animationName, new AnimationChannelCollection(animList));
}
}
错误是:
索引在数组边界之外。
线:0
System.Collections.Generic.Dictionary的2。FindEntry (TKey键)
System.Collections.Generic.Dictionary的2。get_Item (TKey键)
在Xclna.Xna.Animation.InterpolationController.GetCurrentBoneTransform (BonePose姿势)
我本以为KeyNotFoundException错误的关键,但相反,我得到"索引是在数组的边界之外"。我不明白我怎么能从上面的代码得到异常?
这个代码是单线程的。A "索引超出数组边界。"字典(或System.Collections
命名空间中的任何东西)上的错误,当文档说错误不应该被抛出时,总是由违反线程安全引起。
System.Collections
命名空间中的所有集合只允许两种操作中的一种发生
- 无限并发读,0写。
- 0个读取,1个写入。
你必须使用ReaderWriterLockSlim
来同步所有对字典的访问,这就给出了上面描述的行为
private Dictionary<string, BoneKeyFrameCollection> dict =
new Dictionary<string, BoneKeyFrameCollection>();
private ReaderWriterLockSlim dictLock = new ReaderWriterLockSlim();
public BoneKeyFrameCollection this[string boneName]
{
get
{
try
{
dictLock.EnterReadLock();
return dict[boneName];
}
finally
{
dictLock.ExitReadLock();
}
}
}
public void UpdateBone(string boneName, BoneKeyFrameCollection col)
{
try
{
dictLock.EnterWriteLock();
dict[boneName] = col;
}
finally
{
dictLock.ExitWriteLock();
}
}
或将Dictionary<string, BoneKeyFrameCollection>
替换为ConcurrentDictionary<string, BoneKeyFrameCollection>
private ConcurrentDictionary<string, BoneKeyFrameCollection> dict =
new ConcurrentDictionary<string, BoneKeyFrameCollection>();
public BoneKeyFrameCollection this[string boneName]
{
get
{
return dict[boneName];
}
}
public void UpdateBone(string boneName, BoneKeyFrameCollection col)
{
dict[boneName] = col;
}
UPDATE:我真的不明白你所展示的代码是如何导致这个问题的。下面是导致抛出的函数的源代码。
private int FindEntry(TKey key) {
if( key == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (buckets != null) {
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
}
}
return -1;
}
代码抛出ArgumentOutOfRangeException
的唯一方式是如果你试图索引buckets
或entries
中的非法记录。
因为你的键是一个string
,字符串是不可变的,我们可以排除一个键在被放入字典后被改变的hashcode
值。剩下的就是一个buckets[hashCode % buckets.Length]
呼叫和几个entries[i]
呼叫。
buckets[hashCode % buckets.Length]
失败的唯一可能是buckets
的实例在buckets.Length
属性调用和this[int index]
索引器调用之间被替换。buckets
被替换的唯一时间是当Resize
被Insert
内部调用时,Initialize
被构造函数/第一次调用Insert
或调用OnDeserialization
时调用。
Insert
只在this[TKey key]
的setter、Add
的公共函数和OnDeserialization
内部被调用。替换buckets
的唯一方法是,在buckets[hashCode % buckets.Length]
调用期间,FindEntry
调用发生在另一个线程上,同时调用列出的三个函数之一。
我们可以得到一个坏的entries[i]
调用的唯一方法是,如果entries
被交换出我们(遵循与buckets
相同的规则),或者我们得到一个坏的i
值。获得i
错误值的唯一方法是entries[i].next
返回错误值。从entries[i].next
中获得错误值的唯一方法是在Insert
, Resize
或Remove
期间进行并发操作。
我唯一能想到的是OnDeserialization
调用出现问题,并且在反序列化之前开始有坏数据,或者有更多的AnimationChannelCollection
代码影响您没有显示给我们的字典。