C# System.Collections.Generic.KeyNotFoundException:字典中不存在给定的键
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11972337/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary
提问by Programming Newbie
I receive the above error message when performing a unit test on a method. I know where the problem is at, I just don't know why it's not present in the dictionary.
对方法执行单元测试时收到上述错误消息。我知道问题出在哪里,我只是不知道为什么它没有出现在字典中。
Here is the dictionary:
这是字典:
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;
}
}
and here is the method that is being tested:
这是正在测试的方法:
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;
}
The error occurs here:
错误发生在这里:
Denominator = mergedDict[Tuple.Create(groupItem.Key.Year, groupItem.Key.Month, groupItem.Key.LocationName)].TotalHours,
I do not understand why it is not present in the key. The key consists on an int, int, string (year, month, location) and that is what I have assigned it.
我不明白为什么它不存在于密钥中。键由一个整数、整数、字符串(年、月、位置)组成,这就是我分配给它的。
I've looked at all of the other threads concerning this error message but I didn't see anything that applied to my situation.
我已经查看了有关此错误消息的所有其他线程,但没有看到任何适用于我的情况的内容。
I was unsure of what tags to put on this but from my understanding the dictionary was created with linq to xml, the query is linq to sql and it's all part of C# so I used all the tags. if this was incorrect then I apologize in advance.
我不确定要放什么标签,但根据我的理解,字典是用 linq 到 xml 创建的,查询是 linq 到 sql,它都是 C# 的一部分,所以我使用了所有标签。如果这是不正确的,那么我提前道歉。
采纳答案by Kevin Kalitowski
The problem is with comparisons between the keys you are storing in the Dictionaryand the keys you are trying to look up.
问题在于您存储在 中Dictionary的密钥与您尝试查找的密钥之间的比较。
When you add something to a Dictionaryor access the indexer of a Dictionaryit uses the GetHashCode()method to get a hash value of the key. The hashcode for a Tupleis unique to that instance of the Tuple. This means that unless you are passing in the exact same instance of the Tupleclass into the indexer, it will not find the previously stored value. Your usage of mergedDict[Tuple.Create(...creates a brand new Tuple with a different hash code than is stored in the Dictionary.
当您向 a 添加内容Dictionary或访问 a 的索引器时,Dictionary它使用该GetHashCode()方法获取键的哈希值。a 的哈希码对于 的Tuple该实例是唯一的Tuple。这意味着除非您将Tuple类的完全相同的实例传递给索引器,否则它不会找到先前存储的值。您的使用mergedDict[Tuple.Create(...创建了一个全新的元组,其哈希码与存储在Dictionary.
I would recommend creating your own class to use as the key and implementing GetHashCode()and the Equality methods on that class. That way the Dictionary will be able to find what you previously stored there.
我建议创建您自己的类以用作该类的关键和实现GetHashCode()和 Equality 方法。这样,词典将能够找到您之前存储在那里的内容。
More:
The reason this is confusing to a lot of people is that for something like Stringor Int32, String.GetHashCode()will return the same hash code for two different instances that have the same value. A more specialized class such as Tupledoesn't always work the same. The implementor of Tuplecould have gotten the hash code of each input to the Tupleand added them together (or something), but running Tuple through a decompiler you can see that this is not the case.
更多:这让很多人感到困惑的原因是,对于像Stringor 之类的东西Int32,String.GetHashCode()将为具有相同值的两个不同实例返回相同的哈希码。更专业的类,例如Tuple并不总是一样工作。的实现者Tuple可以获取每个输入的哈希码Tuple并将它们添加在一起(或其他东西),但是通过反编译器运行 Tuple 您可以看到情况并非如此。

