为什么这个Linq无法正常工作

时间:2014-10-18 15:46:49

标签: c# linq dictionary nullreferenceexception

我有一个相当丑陋的对象(是的,我需要那里的元组:)):

var roomToIndex = new Dictionary<RoomNode, Tuple<Int32, Dictionary<GrowDirections, Int32>>>();

我像这样初始化这个词典:

for (var i = 0; i < roomsAdjacent.Count(); i++) {
    roomToIndex.Add(roomsAdjacent.ElementAt(i), new Tuple<Int32, Dictionary<GrowDirections, Int32>>(i, new Dictionary<GrowDirections, Int32>()));
    roomToIndex.ElementAt(i).Value.Item2.Add(GrowDirections.Top, 0);
    roomToIndex.ElementAt(i).Value.Item2.Add(GrowDirections.Right, 0);
    roomToIndex.ElementAt(i).Value.Item2.Add(GrowDirections.Bottom, 0);
    roomToIndex.ElementAt(i).Value.Item2.Add(GrowDirections.Left, 0);
}

其中roomsAdjacent是RoomNodes和GrowDirections的列表a [Flags] Enumerable。
在这些初始化步骤之后,我增加'内部'字典的整数值,最后我想得到具有最大值的GrowDirection和RoomNode。
我尝试以下方式(现在):

///Use the Counts to determine optimal FillDirection
var rDC = roomToIndex
    ///Select the RoomNode and the GrowDirection (incl. Count) with the highest Count for each Room
    .Select(r => new { RoomNode = r.Key, Dict = r.Value.Item2.OrderByDescending(dirCount => dirCount.Value).ToList()[0] })
    ///Order those RoomNodes and GrowDirections descending
    ///Take the first Element and used the Dict's Key (GrowthDirection)
    .OrderByDescending(rGI => rGI.Dict.Value).ToList();
var rDC0 = rDC[0];
if (rDC0.Dict.Key == GrowDirections.Top || rDC0.Dict.Key == GrowDirections.Bottom)
    fillDirection = GrowDirections.Top | GrowDirections.Bottom;
else
    fillDirection = GrowDirections.Right | GrowDirections.Left;
foreach (var rTI in roomToIndex.Where(rTI => rTI.Key != rDC0.RoomNode))
    roomCellCount[rTI.Value.Item1] = 0;

rDC的类型为{RoomNode,Dictionary},我没有问题。 但是当我调试并进入下一行时:

var rDC0 = rDC[0];

调试器跳过该行,右转到'if语句'并抛出一个错误,告诉我有一个NullReferenceException ?? !!
当我查看'rDC对象'中的值时,没有null-Value。

它能是什么?感谢您的任何提示/帮助:)

2 个答案:

答案 0 :(得分:1)

检查您的代码rDC的类型是List<KeyValuePair<RoomNode, something very complicated>。重要的不是非常复杂的,而KeyValuePair<TKey, TValue>是一种值类型(struct)。这意味着List<KeyValuePair<TKey, TValue>>不能包含null的元素。这意味着rDC0不能是null。这基本上也是你告诉我们的。

但是,如果您按照自己的描述获得NullReferenceException,则rDC0.Dict必须为null。但是,Dict属性不能为null,因为初始化代码已将其初始化为新的Dictionary<GrowDirections, Int32>

因此,您在问题中提供的代码不应该能够展示您描述的行为。您的代码有所不同,或者您获得的行为与您描述的不完全相同。您提到的调试器问题可能是调试发行版本或符号与可执行代码不同步的结果。

我建议你尝试以下一项或多项措施来解决问题:

  • 重建解决方案以确保调试程序在调试时显示正确的源代码
  • 切换到调试版本以关闭将使调试混乱的优化
  • 将您的数据和代码分解为更小的部分,以摆脱复杂且难以理解的代码

最后一个建议是解决(或已解决)您的问题的方法。我先给大家指点一下:

您可以创建一个包含四个属性的类型,而不是使用Dictionary<GrowDiretions, Int32>,这有望使您的代码更清晰:

class GrowCounts {
  public Int32 TopCount { get; set; }
  public Int32 RightCount { get; set; }
  public Int32 BottomCount { get; set; }
  public Int32 LeftCount { get; set; }
  public GrowDirections MaxGrowDirection {
    get { // Return GrowDirections.Top if TopCount has the highest count etc. }
  }
}

而不是使用new Tuple<T1, T2>使用Tuple.Create让编译器推断出元组的类型。

你真的需要第一个元素是索引的Tuple吗?您的一些代码使用for CountElementAt的循环,并且以这种方式访问​​集合需要索引。但是,也许你可以将这些循环转换为foreach循环,在这个过程中你会发现索引是不需要的。如果可能的话,你可以摆脱Tuple

答案 1 :(得分:0)

显然(根据你写的内容)它与复杂的Linq语句有关,它有一个副作用,它以某种方式导致空引用错误,它将它放在它自己的函数中。通过查看在初始化之前放置更简单的Linq Select语句会发生什么,您可能能够获得更多关于究竟是什么原因导致的线索。请参阅马丁后来关于如何追踪这里实际情况的更全面的建议。