C#Foreach循环给出Null-error

时间:2012-12-18 19:02:31

标签: c# exception foreach null

我正在写一些代码,有一部分让我烦恼。 我的代码中有以下结构:

foreach (Node node in nodes)
{
    try
    {
        bool n = node.previous == null;
    }
    catch (Exception e)
        {
            StreamWriter s = new StreamWriter("error.txt");
            s.WriteLine(e.Message);
            s.WriteLine("-----------");
            s.Close();
        }
}

此处Node是自定义类型,node.prev变量也是自定义类型(Location)。运行此命令会显示以下错误日志:

Object reference not set to an instance of an object.
-----------

我100%确定该节点不为空(应该通过foreach使其不可能 =不是,抱歉!)。此外,如下面的清单所示,Node.previous在声明时设置为null:

public class Node
{
    public Location previous = null;
    public Location location;
    public int distance;
    ...
    ...
}

我不知道如何解决这些出现的问题,而且我对如何解决这个问题有任何想法。有人可以帮我吗?

请注意,这些都不是最终的代码,但我筛选了无关紧要的部分!

提前致谢, Delpee

编辑:Ricovox帮我解决了this answer的问题,非常感谢!

6 个答案:

答案 0 :(得分:4)

最可能的问题是node null!。 foreach语句没有任何内容可以阻止node为空,因为通常IEnumerable对象(例如列表或集合) CAN 包含null作为有效项。

如果previous为null,则不会抛出任何错误(除非node.prev类/结构覆盖==运算符)。

正如其他人所提到的那样,进行这样的测试来验证:

foreach (Node node in nodes)
{
    bool n;
    try
    {
        if (node == null) 
        {
           n = true; //do something to deal with a null node
        }
        else 
        {
          n = node.previous == null;
        }
    }
    catch (Exception e)
    {
        StreamWriter s = new StreamWriter("error.txt");
        s.WriteLine(e.Message);
        s.WriteLine("-----------");
        s.Close();
    }
}

答案 1 :(得分:4)

所以我在回复你的评论时发布了另一个答案:

  

“我已经检查过节点是否为null,它在任何时候都没有。”

如果这是真的,那么还有其他一些选择;

  1. 查看node.previous类/结构定义。它可以实现一个CUSTOM ==运算符,与null相比,它会抛出一个错误。例如,考虑Nullable<T>类型。它有一个重载的==运算符,如果HasValue属性为false,则返回true(显然,因为它是一个结构,它不是真正的null,但重载运算符会产生所需的行为。)为了解决这个问题,你可以测试object.ReferenceEquals(node.previous, null)哪个不会超载。如果您有权更改node.previous定义,也许您可​​以找出为什么重载运算符在与null(显然不应该)相比时抛出异常

  2. nodes可以为null,在这种情况下,它是抛出错误的foreach语句。在foreach之前测试nodes == null

  3. <强> ---------------编辑---------------

    正如Jeppe指出的那样,我与Nullable<T>的比喻(在上面的选项#1中)可能会产生误导(实际上它确实混淆了讨论,尽管这一点本身是正确的)。为了更好地说明覆盖==运算符的想法,我在下面发布了一个演示Container类型的示例。这种类型是一个结构,因此它本身永远不能为null,但它包含一个Value对象。 (这个结构的要点是你可以使用任何Container对象而不用担心它是否为null,即使包含的Value对象可能为null)。需要注意的主要事项是,当一个Container与==进行比较为null时,如果Valuenull,则结果为true,如果==则不为真运算符未被覆盖(因为结构永远不能为空)。

    public struct Container {
        public object Value { get; set; }
        public bool IsNull { get { return Value == null; } }
        public static bool operator ==(Container x, object y) { return x.Equals(y); }
        public static bool operator !=(Container x, object y) { return !x.Equals(y); }
        public override bool Equals(object obj) {
            if (obj is Container)
                return Value == ((Container)obj).Value;
            return Value == obj;
        }
        public override int GetHashCode() { return Value == null ? 0 : Value.GetHashCode(); }
    }
    
    ////---------Test----------
    var x = new Container { Value = null };
    var y = new Container { Value = "hi" };
    var z = new Container { Value = null };
    Print(x == null); //true 
    Print(x == y);    //false
    Print(x == z);    //true
    

    如果你想知道覆盖==将如何影响一个类,我在下面写了另一个演示Box类的例子。它类似于Container结构,除了我们必须处理Box对象本身为null的情况。请注意,在这种情况下,覆盖==还有另一个令人惊讶的结果。两个引用类型可以相互相等(==),即使它们引用不同的对象,只要它们具有相同的Value属性。

    public class Box {
        public static bool ItemsEqual(object x, object y) {
            object xval, yval;
            xval = x is Box ? (x as Box).Value : x;
            yval = y is Box ? (y as Box).Value : y;
            return xval == yval;
        }
        public object Value { get; set; }
        public bool IsNull { get { return Value == null; } }
        public static bool operator ==(Box x, object y) { return ItemsEqual(x, y); }
        public static bool operator !=(Box x, object y) { return !ItemsEqual(x, y); }
        public override bool Equals(object obj) { return ItemsEqual(this, obj); }
        public override int GetHashCode() { return Value == null ? 0 : Value.GetHashCode(); }
    }
    
    ////---------Test----------
    object n = null;
    Box w = null;
    Box x = new Box { Value = null };
    Box y = new Box { Value = "hi" };
    Box z = new Box { Value = "hi" };
    
    Print(w == null);  //true (uses overridden '==' because w is defined as a Box)
    Print(w == n);     //true
    Print(x == w);     //true 
    Print(x == null);  //true 
    Print(x == n);     //true 
    
    Print(w == y);    //false
    Print(x == y);    //false
    Print(y == z);    //true (actual ref's differ, but values are ==)
    

答案 2 :(得分:1)

如果nodesnull,则在循环之前会发生异常,并且不会捕获异常。

由于previous成员是一个简单的字段,我看到的唯一可能性是node集合中的nodes个成员之一是null。你可以这样做:

foreach (Node node in nodes)
{
  if (node == null)
    throw new Exception("I told you so");
}

编辑:好的,事实证明我有一种可能性没有看到,这是==运算符的重载。为了确保你调用通常的过载,请说:

foreach (Node node in nodes)
{
  bool n = (object)(node.previous) == (object)null;
}

(实际上只需将==运算符的一侧强制转换为object),或等效地使用ReferenceEquals

foreach (Node node in nodes)
{
  bool n = object.ReferenceEquals(node.previous, null);
}

必须修复==重载的错误实现。

如果您在原始问题中发布了异常的堆栈跟踪,那么大多数用户都可以清楚地看到异常来自对==重载的调用。所以下次检查堆栈跟踪。

答案 3 :(得分:0)

没有什么可以确保node不为空,因为Node不是值类型。介意提供更多细节?

答案 4 :(得分:0)

在您的行中设置以bool开头的断点,并验证该节点不为空且该节点不为空。我的猜测是两个节点都是null,否则它包含一个空值,如果它不是一个类型安全的列表。

答案 5 :(得分:0)

  

我100%确定该节点不为空

但我100%确定节点中的元素为空!试试这个:

foreach (Node node in nodes)
{
    try
    {
        if(null == node) throw new Exception("This is not expected!");
        bool n = node.previous == null;
    }
    catch (Exception e)
        {
            if(File.Exists("error.txt")) File.Delete("error.txt");
            using(StreamWriter s = new StreamWriter("error.txt")){
            s.WriteLine(e.Message);
            s.WriteLine("-----------");
            s.Close();}
        }
}