c#字典 - “值不在预期范围内”

时间:2013-09-07 20:15:03

标签: c# windows-phone-7 windows-phone-8 argumentexception

在我的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();
}

我的主要问题是我不明白导致异常的原因。在过去的两天里,我一直认为这条消息只是意味着它试图将keyvaluepair添加到密钥已经存在的字典中。但是此缓存会在密钥是否已存在之前检查,并在此情况下返回缓存值。我写了几个详细的单元测试,有数千个插入,没有任何反应。

堆栈跟踪中的奇怪之处在于GetWorkDay()之后调用了Dictionary2.Insert()。但是我发现有重复密钥问题的所有堆栈跟踪之前都调用了Dictionary2.Add()(我实际上是在代码中执行的,因为我无法直接调用Insert()。

那么有什么我想念的可能会引发这种异常吗?

还有更多要了解的事情:

_cachedWorkDays是唯一具有键类型日期和值类型WorkDay

的字典

Date是我自己的日期实现(需要更多的方法来处理日期而不是DateTime提供给我。此外,我想确保DateTime中的时间部分不影响我的日期处理)。因为我在字典中使用Date作为键,所以需要Equals和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 个答案:

答案 0 :(得分:1)

可能的原因#1:并发问题。当线程A正在执行_databaseController.Wait()时,线程B将那一天添加到缓存中,在线程A唤醒之后,您将看到完全相同的异常。

可能的原因#2:你的Date类是可变的。使用可变数据类型作为键将完全搞乱字典。请注意System.DateTime是(a)不可变(b)struct(c)实现IEquatable<DateTime>

P.S。使用dict.TryGetValue(..) API比dict.ContainsKey(..)和后续dict[..]

更有效