System.Collections.Generic.KeyNotFoundException:给定的键不在字典中

我在对方法执行unit testing时收到上述错误消息。 我知道问题出在哪里,我只是不知道为什么它不在字典中。

这是字典:

var nmDict = xelem.Descendants(plantNS + "Month").ToDictionary( k => new Tuple(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, 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.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 QueryData(HarvestTargetTimeRangeUTC ranges, IDictionary<Tuple, 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 ListOfResults = new List(); 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  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到xml创建的,查询是linq到sql,它是C#的所有部分所以我使用了所有的标签。 如果这不正确,那么我提前道歉。

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

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

我建议创建自己的类作为键,并在该类上实现GetHashCode()和Equality方法。 通过这种方式,词典将能够找到您之前存储的内容。

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