【问题标题】:System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionarySystem.Collections.Generic.KeyNotFoundException:给定的键不在字典中
【发布时间】:2013-12-04 05:04:10
【问题描述】:

我在对方法执行单元测试时收到上述错误消息。我知道问题出在哪里,只是不知道为什么字典中没有。

这是字典:

var nmDict = xelem.Descendants(plantNS + "Month").ToDictionary(
    k => new Tuple<int, int, string>(int.Parse(k.Ancestors(plantNS + "Year").First().Attribute("Year").Value), Int32.Parse(k.Attribute("Month1").Value), k.Ancestors(plantNS + "Report").First().Attribute("Location").Value.ToString()),
    v => { 
             var detail = v.Descendants(plantNS + "Details").First();
             return new HoursContainer
             {
                 BaseHours = detail.Attribute("BaseHours").Value,
                 OvertimeHours = detail.Attribute("OvertimeHours").Value,
                 TotalHours = float.Parse(detail.Attribute("BaseHours").Value) + float.Parse(detail.Attribute("OvertimeHours").Value)
             };
         });

var mergedDict = new Dictionary<Tuple<int, int, string>, HoursContainer>();

foreach (var item in nmDict)
{
    mergedDict.Add(Tuple.Create(item.Key.Item1, item.Key.Item2, "NM"), item.Value);
}


var thDict = xelem.Descendants(plantNS + "Month").ToDictionary(
    k => new Tuple<int, int, string>(int.Parse(k.Ancestors(plantNS + "Year").First().Attribute("Year").Value), Int32.Parse(k.Attribute("Month1").Value), k.Ancestors(plantNS + "Report").First().Attribute("Location").Value.ToString()),
    v => {
             var detail = v.Descendants(plantNS + "Details").First();
             return new HoursContainer
             {
                 BaseHours = detail.Attribute("BaseHours").Value,
                 OvertimeHours = detail.Attribute("OvertimeHours").Value,
                 TotalHours = float.Parse(detail.Attribute("BaseHours").Value) + float.Parse(detail.Attribute("OvertimeHours").Value)
             };
         });

foreach (var item in thDict)
{
    mergedDict.Add(Tuple.Create(item.Key.Item1, item.Key.Item2, "TH"), item.Value);
}
        return mergedDict;                                 

}

这是正在测试的方法:

protected IList<DataResults> QueryData(HarvestTargetTimeRangeUTC ranges,
        IDictionary<Tuple<int, int, string>, HoursContainer> mergedDict)
{            
    var startDate = new DateTime(ranges.StartTimeUTC.Year, ranges.StartTimeUTC.Month, 1);
    var endDate = new DateTime(ranges.EndTimeUTC.Year, ranges.EndTimeUTC.Month, 1);
    const string IndicatorName = "{6B5B57F6-A9FC-48AB-BA4C-9AB5A16F3745}";

    DataResults endItem = new DataResults();
    List<DataResults> ListOfResults = new List<DataResults>();                               

    var allData =

    (from vi in context.vDimIncidents
    where vi.IncidentDate >= startDate.AddYears(-3) && vi.IncidentDate <= endDate
        select new
        {
            vi.IncidentDate,
            LocationName = vi.LocationCode,
            GroupingName = vi.Location,
            vi.ThisIncidentIs, vi.Location
        });

    var finalResults = 

            (from a in allData
            group a by new { a.IncidentDate.Year, a.IncidentDate.Month, a.LocationName, a.GroupingName, a.ThisIncidentIs, a.Location }
                into groupItem
            select new 
            {
                Year = String.Format("{0}", groupItem.Key.Year),
                Month = String.Format("{0:00}", groupItem.Key.Month),
                groupItem.Key.LocationName,
                GroupingName = groupItem.Key.GroupingName,
                Numerator = groupItem.Count(),
                Denominator = mergedDict[Tuple.Create(groupItem.Key.Year, groupItem.Key.Month, groupItem.Key.LocationName)].TotalHours,  
                IndicatorName = IndicatorName,                        
            }).ToList();


    for (int counter = 0; counter < finalResults.Count; counter++)
    {
        var item = finalResults[counter];
        endItem = new DataResults();
        ListOfResults.Add(endItem);
        endItem.IndicatorName = item.IndicatorName;
        endItem.LocationName = item.LocationName;
        endItem.Year = item.Year;
        endItem.Month = item.Month;
        endItem.GroupingName = item.GroupingName;
        endItem.Numerator = item.Numerator;
        endItem.Denominator = item.Denominator;               
    }

    foreach(var item in mergedDict)
    {
        if(!ListOfResults.Exists(l=> l.Year == item.Key.Item1.ToString() && l.Month == item.Key.Item2.ToString()
                && l.LocationName == item.Key.Item3))
        {
            for (int counter = 0; counter < finalResults.Count; counter++)
            {
                var data = finalResults[counter];
                endItem = new DataResults();
                ListOfResults.Add(endItem);
                endItem.IndicatorName = data.IndicatorName; 
                endItem.LocationName = item.Key.Item3;
                endItem.Year = item.Key.Item1.ToString();
                endItem.Month = item.Key.Item2.ToString();
                endItem.GroupingName = data.GroupingName; 
                endItem.Numerator = 0;
                endItem.Denominator = item.Value.TotalHours;
            }
        }
    }
    return ListOfResults;
}

错误发生在这里:

 Denominator = mergedDict[Tuple.Create(groupItem.Key.Year, groupItem.Key.Month, groupItem.Key.LocationName)].TotalHours,  

我不明白为什么它不存在于密钥中。键由 int、int、string(年、月、位置)组成,这就是我分配给它的。

我查看了有关此错误消息的所有其他线程,但我没有看到任何适用于我的情况的内容。

我不确定要在上面放什么标签,但据我了解,字典是用 linq to xml 创建的,查询是 linq to sql,它都是 C# 的一部分,所以我使用了所有标签。如果这是不正确的,那么我提前道歉。

【问题讨论】:

  • 请发布一个小的可重现示例供我们查看。
  • @asawyer 请用可重现的例子解释你的意思
  • 字符串的“相等”形式是否与字典的 IEqualityComparer 匹配?例如。您的键匹配是否在给定语言环境中不区分大小写,但字典在另一个语言环境中查找不区分大小写,等等?
  • 小 = 大约 7 行(至少不是 2 个带有滚动条的大块),可重现 = 通过运行样本(所有 7 行)或将样本插入一些基本的 C# 程序/LINQPad
  • @Alexei Levenkov 如果我知道如何在七行中获得与我正在寻找的结果相同的结果,那么我首先会在七行中创建它。仅我的最终字典就超过七行。

标签: c# linq-to-sql linq-to-xml idictionary


【解决方案1】:

问题在于您存储在Dictionary 中的密钥与您尝试查找的密钥之间的比较。

当您向Dictionary 添加内容或访问Dictionary 的索引器时,它使用GetHashCode() 方法获取键的哈希值。 Tuple 的哈希码对于 Tuple 的该实例是唯一的。这意味着除非您将完全相同的 Tuple 类实例传递给索引器,否则它将找不到先前存储的值。您对mergedDict[Tuple.Create(... 的使用会创建一个全新的元组,其哈希码与Dictionary 中存储的哈希码不同。

我建议您创建自己的类以用作键并在该类上实现GetHashCode() 和 Equality 方法。这样,词典就能找到您之前存储的内容。

更多: 这让很多人感到困惑的原因是,对于StringInt32 之类的东西,String.GetHashCode() 将为具有相同值的两个不同实例返回相同的哈希码。 Tuple 等更专业的类并不总是一样的。 Tuple 的实现者本可以将每个输入的哈希码获取到 Tuple 并将它们加在一起(或其他东西),但是通过反编译器运行 Tuple,您会发现情况并非如此。

【讨论】:

  • 非常感谢您的帮助。
  • 如果您使用 ReSharper,您可以按 Alt+Ins 并让它为您生成所有 GetHashCode()、Equals() 等方法。他们仍然需要一些手工工作,但可以节省大量时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-14
  • 2012-02-18
  • 1970-01-01
相关资源
最近更新 更多