Yuri van Geffen
Yuri van Geffen

Reputation: 983

C# Foreach-loop giving Null-error

I'm writing a bit of code, and there is a part that gives me hassles. I'm having the following structure in my code:

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();
        }
}

In here Node is a custom type, the node.prev variable is also a custom type (Location). Running this gives the following error-log:

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

I'm 100% sure that node isn't null (which should be made impossible by foreach = Not true, sorry!). Also, as you can see in the following listing, the Node.previous is set to null at declaration:

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

I have no idea how I can fix these exeptions occuring, and I'm kind of at the end of having any ideas on how to solve this. Can anybody help me with this?

Note that these are all not the final code, but I sifted the parts that didn't matter!

Thanks in advance, Delpee

EDIT: Ricovox helped me to the solution with this answer, thanks a lot!

Upvotes: 0

Views: 2217

Answers (6)

drwatsoncode
drwatsoncode

Reputation: 5223

So I'm posting another answer in response to your comment:

"I have checked for node being null, it isn't, at any point."

If that IS true, there here are some other options;

  1. Take a look at the node.previous class/struct definition. It COULD implement a CUSTOM == operator that throws an error when compared to null. As an example, think about the Nullable<T> type. It has an overloaded == operator that returns true if the HasValue property is false (obviously, since it is a struct it isn't truly null, but overloading the operator gives the desired behavior.) To fix this, you could test object.ReferenceEquals(node.previous, null) which wouldn't be overloaded. If you have access to change the node.previous definition, perhaps you can find out why the overloaded operator throws an exception when compared to null (which it obviously shouldn't)

  2. nodes could be null, in which case it is the foreach statement that is throwing the error. Test nodes == null before the foreach.

---------------Edit---------------

As Jeppe pointed out, my analogy with Nullable<T> (in option #1, above) could be misleading (in fact it did confuse the discussion, although the point itself was correct). In order to better illustrate the idea of overriding the == operator, I've posted an example below that demonstrates the Container type. This type is a struct, so it can never be null itself, but it contains a single Value object. (The point of this struct is that you can work with any Container object without worrying whether or not it is null, even though the contained Value object might be null). The main thing to note is that when a Container is compared with == to null, the result will be true if Value is null, which would NOT be true if the == operator weren't overridden (because a struct can never be null).

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

And just in case you are wondering how overriding == would affect a class, I've written another example below that demonstrates a Box class. It's similar to the Container struct, except that we have to deal with the case in which the Box object is null itself. Note that in this case, there is another surprising result of overriding ==. Two reference types can be equal (==) to each other, even if they reference different objects, as long as they have equal Value properties.

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 ==)

Upvotes: 4

Jeppe Stig Nielsen
Jeppe Stig Nielsen

Reputation: 61912

If nodes were null the exception would occur before the loop, and the exception wouldn't be caught.

Since the previous member is a simple field, the only possibility I see is that one of the node members of your nodes collection is null. You can do this:

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

EDIT: OK, it turned out there was one possibility I didn't see, and that was a broken overload of the == operator. To make sure you call the usual overload, say:

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

(actually it will suffice to cast just one side of the == operator to object), or equivalently use ReferenceEquals:

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

But you must fix the wrong implementation of the == overload.

If you had posted the stack trace of the exception in your original question, it would have been clear to most users here on SO that the exception came from a call to your == overload. So next time, inspect your stack trace.

Upvotes: 1

drwatsoncode
drwatsoncode

Reputation: 5223

The most likely problem is that node is null!. There is nothing about the foreach statement that would prevent node from being null, because in general an IEnumerable object (e.g. a list or collection) CAN contain null as a valid item.

No error would be thrown if previous is null (unless the node.prev class/struct overrides the == operator).

As others have mentioned, put in a test like this to verify:

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();
    }
}

Upvotes: 4

Yasser Zamani
Yasser Zamani

Reputation: 2500

I'm 100% sure that node isn't null

But I'm 100% sure that an element in nodes is null! try this:

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();}
        }
}

Upvotes: 0

e_ne
e_ne

Reputation: 8459

There's nothing that ensures that node is not null, since Node is not a value type. Mind giving more details?

Upvotes: 0

Paul
Paul

Reputation: 36319

set a breakpoint at your line that starts with bool and verify that node is not null and that nodes is not null. My guess is that either nodes is null, or else it contains a null value if it's not a type-safe list.

Upvotes: 0

Related Questions