System.Collections.Generic.KeyNotFoundException:字典中不存在给定的键 [英] System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary

查看:214
本文介绍了System.Collections.Generic.KeyNotFoundException:字典中不存在给定的键的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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.

这是字典:

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,  

我不明白为什么键中不存在它.密钥包含一个整数,整数,字符串(年,月,位置),这就是我为其分配的内容.

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.

我不确定要在上面加上什么标签,但是据我了解,字典是使用linq到xml创建的,查询是linq到sql的,并且都是C#的一部分,因此我使用了所有标签.如果这是不正确的,那么我提前致歉.

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.

推荐答案

问题在于存储在Dictionary中的键与要查找的键之间的比较.

The problem is with comparisons between the keys you are storing in the Dictionary and the keys you are trying to look up.

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

When you add something to a Dictionary or access the indexer of a Dictionary it uses the GetHashCode() method to get a hash value of the key. The hashcode for a Tuple is unique to that instance of the Tuple. This means that unless you are passing in the exact same instance of the Tuple class 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.

我建议创建自己的类用作键,并在该类上实现GetHashCode()和Equality方法.这样,字典将能够找到您以前存储在其中的内容.

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.

更多: 这使很多人感到困惑的原因是对于StringInt32之类的东西,String.GetHashCode()将为具有相同值的两个不同实例返回相同的哈希码.诸如Tuple之类的更专业的类并非总是能正常工作. Tuple的实现者本可以将每个输入的哈希码获取到Tuple并将其添加在一起(或类似的东西),但是通过反编译器运行Tuple可以看到情况并非如此.

More: The reason this is confusing to a lot of people is that for something like String or Int32, String.GetHashCode() will return the same hash code for two different instances that have the same value. A more specialized class such as Tuple doesn't always work the same. The implementor of Tuple could have gotten the hash code of each input to the Tuple and added them together (or something), but running Tuple through a decompiler you can see that this is not the case.

这篇关于System.Collections.Generic.KeyNotFoundException:字典中不存在给定的键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆