c# 字典 - “值不在预期范围内”
本文关键字:范围内 字典 | 更新日期: 2023-09-27 18:30:28
在我的 WP7/8 应用程序中,我有时会收到来自用户的以下错误消息(无法在此处重现该问题)。
[Type]:[ArgumentException]
[ExceptionMessage]:[Value does not fall within the expected range.]
[StackTrace]:[
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(Date key, WorkDay value, Boolean add)
at MyProject.Core.Data.WorkDayCache.GetWorkDay(Date date, Boolean returnCorrected)
at MyProject.Core.Calculations.CalculationHelper.WorkTimeDay(Date date)
at MyProject.WP.UI.InfoBoxes.IBWorkTimeToday.UpdateMinute()
at MyProject.WP.UI.InfoBoxes.IBWorkTimeToday.Update()
at MyProject.WP.UI.MainPage.UpdateInfoBoxes()
at MyProject.WP.UI.MainPage.ButtonStart_Click(Object sender, RoutedEventArgs e)
at System.Windows.Controls.Primitives.ButtonBase.OnClick()
at System.Windows.Controls.Button.OnClick()
at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
at System.Windows.Controls.Control.OnMouseLeftButtonUp(Control ctrl, EventArgs e)
at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName)
]
[InnerException]:[none]
下面是 GetWorkDay 方法的代码:
/// <summary>
/// Returns the cached work day for a given date
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public WorkDay GetWorkDay(Date date, bool returnCorrected = true)
{
// return the cached value in case it is cached and the filter is disabled
if (!TagFilter.IsFilterEnabled)
{
if (_cachedWorkDays.ContainsKey(date))
{
if (returnCorrected)
{
var correctedWorkDay = new TimeCalculatorInterface().GetCorrectedWorkDay(_cachedWorkDays[date]);
return correctedWorkDay;
}
return _cachedWorkDays[date];
}
}
// nothing cached, thus get the result and cache it
var workDays = _databaseController.Wait().GetWorkDays(date, date, false);
if (workDays != null && workDays.Count > 0)
{
if (!TagFilter.IsFilterEnabled)
_cachedWorkDays.Add(date, workDays[0]);
// correct the work day times with the break times if enabled
if (returnCorrected)
{
var correctedWorkDay = new TimeCalculatorInterface().GetCorrectedWorkDay(workDays[0]);
return correctedWorkDay;
}
return workDays[0];
}
return new WorkDay();
}
我的主要问题是我不明白是什么导致了异常。在过去的两天里,我一直觉得这条消息只是意味着它试图将键值对添加到已经存在键的字典中。但是此缓存会在密钥是否已存在之前检查,并在这种情况下返回缓存的值。我用数千个插入编写了几个详细的单元测试,但没有任何反应。
堆栈跟踪中奇怪的是,在 GetWorkDay() Dictionary2.Insert() 之后立即被调用。但是我发现的所有具有重复键问题的堆栈跟踪之前都调用了 Dictionary2.Add()(我实际上在代码中这样做,因为我无法直接调用 Insert()。
那么我错过了什么可能会引发此异常吗?
还有一些事情需要了解:
_cachedWorkDays是唯一具有键类型日期和值类型WorkDay的字典
Date 是我自己对日期的实现(需要比 DateTime 提供给我的更多方法来处理日期。此外,我想确保 DateTime 中的时间部分不会影响我的日期处理)。当我使用日期作为字典中的键时,它需要一个等于和 GetHashCode 覆盖,如下所示)
public static bool operator ==(Date d1, Date d2)
{
return d1.Day == d2.Day && d1.Month == d2.Month && d1.Year == d2.Year;
}
public override bool Equals(object obj)
{
if (obj.GetType() == this.GetType())
{
Date obj1 = (Date)obj;
return obj1 == this;
}
return false;
}
public override int GetHashCode()
{
return (Year*100 + Month)*100 + Day;
}
任何帮助都非常感谢。
问候,斯蒂芬
可能的原因 #1:并发问题。当线程 A 执行 _databaseController.Wait()
时,线程 B 已将该天添加到缓存中,在线程 A 唤醒后,您将看到完全相同的异常。
可能的原因#2:您的Date类是可变的。使用可变数据类型作为键将完全搞砸字典。请注意System.DateTime
(a)不可变(b)结构(c)实现IEquatable<DateTime>
。
附言:使用dict.TryGetValue(..)
API比dict.ContainsKey(..)
和后续dict[..]
更有效