8 Replies - 2299 Views - Last Post: 12 November 2009 - 08:33 AM Rate Topic: -----

#1 mandangalo  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 19
  • Joined: 29-July 09

foreach loop crashes because of null elements in array

Posted 11 November 2009 - 02:37 PM

I am writing a program for my C# class that has us building a program involving the periodic table. One of the methods we need to include goes through and prints all the elements of the table that have data for them. However when the program goes and check each element of my array it gives me a NullReferenceException was unhandled error and I do not know how to catch this error so that it can continue on with the iteration of the foreach loop. Is there anybody who may be able to help me figure out the syntax for picking out this error? None of the online databases have been much help at all.
The foreach loop exists in the PeriodicTable class under the getElement() method.

using System;

namespace StarterFiles
{
	class PeriodicTable
	{
		Element[] m_theElements;

		public PeriodicTable()
		{
			m_theElements = new Element[118]; // I think there are 113....changed to 118 to reflect more accurate periodic table

			// create new elements here, as needed/desired:
			m_theElements[0] = new Element(1, "Hydrogen", "H", 1.0008);
			m_theElements[26] = new Element(27, "Cobalt", "Co", 58.9332);
			m_theElements[70] = new Element(71, "Lutetium", "Lu", 174.96);
			m_theElements[89] = new Element(90, "Protactinium", "Pa", 231.0359);

			// You'll need (at least) 6 more.
			// Pick them so that you can test your program

			// Don't worry about filling in more than 10, total, unless you
			// need more to test the program.
		}

		/// <summary>
		/// You might find this useful, since you're only required to fill 
		/// in 10 elements, and yet there are many more in table...
		/// </summary>
		/// <param name="atomicNum"></param>
		/// <returns>
		/// Return true if the table has data for a given element;
		/// return false otherwise
		/// </returns>
		public bool ElementHasData(int atomicNum)
		{
			// STUDENTS: YOU NEED TO WRITE YOUR CODE BELOW:
			if (m_theElements[atomicNum] != null)
				return true;
			else
				return false;

			// STUDENTS: YOU NEED TO WRITE YOUR CODE ABOVE:
		}

		public int MaxAtomicNumber
		{
			get
			{
				// STUDENTS: YOU NEED TO WRITE YOUR CODE BELOW:

				return m_theElements.Length;

				// STUDENTS: YOU NEED TO WRITE YOUR CODE ABOVE:
			}
		}

		/// <summary>
		/// This is an "indexer"
		/// </summary>
		/// <param name="atomicNum">The atomic number of the element that we're
		/// accessing.  Note that Hydrogen, the first element, is 1 (not zero).
		/// This method will take care of any 're-mapping' that needs to happen</param>
		/// <returns></returns>
		public Element getElement(int atomicNum)
		{

			// STUDENTS: YOU NEED TO WRITE YOUR CODE BELOW:

			foreach (Element e in m_theElements)
			{
				if (e.getAtomicNumber() == atomicNum)
					return e;
			}
			return null;

			// STUDENTS: YOU NEED TO WRITE YOUR CODE ABOVE:
		}

		public void setElement(int atomicNum, Element e)
		{
			// STUDENTS: IMPLEMENT THIS!!
		}
	}
}



Is This A Good Question/Topic? 0
  • +

Replies To: foreach loop crashes because of null elements in array

#2 modi123_1  Icon User is online

  • Suitor #2
  • member icon



Reputation: 8920
  • View blog
  • Posts: 33,443
  • Joined: 12-June 08

Re: foreach loop crashes because of null elements in array

Posted 11 November 2009 - 02:41 PM

wrap your if statement with another if..

IF e is not null
-> do your other if statement.
Was This Post Helpful? 1
  • +
  • -

#3 mandangalo  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 19
  • Joined: 29-July 09

Re: foreach loop crashes because of null elements in array

Posted 11 November 2009 - 03:18 PM

That solution works reeeaallly well when trying to print out one of the array's elements that has data, or doesn't, but when I am trying to print the who array it still gives me that error and only prints the data that exists in the first element of the array. There have been some other elements that have been defined by the teacher that should print out as well but they don't seem to. It also goes up to the 115th element in the array and then crashes with the same error at m_theTable.getElement(atomicNum).Print(). Here are the files that I am working with so that there is more detail in what is going on. Would a try catch block possibly correct this? and if so that's what I was referring to when mentioning that the databases I searched through didn't include much information on error catching.

using System;

namespace StarterFiles
{
	/// <summary>
	/// This class serves as the 'User Interface', or UI, to 
	/// the PeriodicTable class.
	/// </summary>
	class PeriodicTableUI
	{
		PeriodicTable m_theTable;
		private const int QUIT = 0;
		private const int PRINTALL = 1;
		private const int PRINTONE = 2;
		private const int ADDELEMENT = 3;
		private const int EDITELEMENT = 4;

		/// <summary>
		/// The constructor will create a single instance
		/// of the PeriodicTable class
		/// </summary>
		public PeriodicTableUI()
		{
			m_theTable = new PeriodicTable();
		}

		public void RunProgram()
		{
			int userChoice;

			Console.WriteLine("Welcome to the Periodic Table Program!\n\n");

			do
			{
				Console.WriteLine("\nPlease select from the following menu of options:");
				Console.WriteLine("0) Quit the program");
				Console.WriteLine("1) Print all the elements");
				Console.WriteLine("2) Print out a particular element");
				Console.WriteLine("3) Add a new element");
				Console.WriteLine("4) Edit an existing element");

				userChoice = getValidUserInput(0, 4);
				switch (userChoice)
				{
					case PRINTALL:
						printAllElements();
						break;

					case PRINTONE:
						printOneElement();
						break;

					case ADDELEMENT:
						addElementGetInput();
						break;

					case EDITELEMENT:
						editElement();
						break;

					case QUIT:
						// empty, but prevents us from falling into the 'default' case
						break;
					default:
						Console.WriteLine("ERROR: You've selected an option which isn't supported!");
						break;
				}
			}
			while (userChoice != QUIT);

			Console.WriteLine("\nThank you for using the Periodic Table Program!");
			Console.WriteLine("(Please press the <Enter> key to exit)");
			Console.ReadLine();
		}

		private void printAllElements()
		{
			for (int atomicNum = 0; atomicNum <= m_theTable.MaxAtomicNumber; atomicNum++)
			{
				if (m_theTable.ElementHasData(atomicNum))
					Console.WriteLine("Cannot print data: Missing");
				else
				m_theTable.getElement(atomicNum).Print();
			}
			// STUDENTS: IMPLEMENT THIS!!
		}

		private void printOneElement()
		{
			Console.WriteLine("What is the atomic number of the element you would like to print?");
			Console.WriteLine("Valid atomic numbers range from 1 to {0}", m_theTable.MaxAtomicNumber);
			int atomicNum = getValidUserInput(1, m_theTable.MaxAtomicNumber);
			
			if (m_theTable.ElementHasData(atomicNum - 1))
			{
				m_theTable.getElement(atomicNum).Print();
				
			}
			else
			{
				Console.WriteLine("Cannot print data: Missing");
			}

			// STUDENTS: IMPLEMENT THIS!!
		}

		// These are provided for your use
		private void addElementGetInput()
		{
			Console.WriteLine("What is the atomic number of the element you would like to add?");
			Console.WriteLine("Note that you can't replace an existing element");
			Console.WriteLine("Valid atomic numbers range from 1 to {0}", m_theTable.MaxAtomicNumber);
			int atomicNum = getValidUserInput(1, m_theTable.MaxAtomicNumber);
			addElement(atomicNum);
		}

		private void addElementIfUserWantsTo(int atomicNum)
		{
			Console.WriteLine("There is no data for that element!");
			Console.WriteLine("Would you like to add it now? (type 'y' or 'Y' to do so!)");
			char letter = (char)Console.Read();
			Console.ReadLine(); // clear input buffer
			if ('y' == letter || 'Y' == letter)
				addElement(atomicNum);
		}

		private void addElement(int atomicNum)
		{
			if (m_theTable.ElementHasData(atomicNum))
				Console.WriteLine("This element has already been defined");
			else
			{

			}
		}

		private void editElement()
		{
			Console.WriteLine("What is the atomic number of the element you would like to edit?");
			Console.WriteLine("Valid atomic numbers range from 1 to {0}", m_theTable.MaxAtomicNumber);
			int atomicNum = getValidUserInput(1, m_theTable.MaxAtomicNumber);

			// STUDENTS: IMPLEMENT THIS!!
		}

		private double getValidUserInputDouble(double min, double max)
		{
			double userChoice;
			bool notANum;
			while ((notANum = !Double.TryParse(Console.ReadLine(), out userChoice)) ||
				userChoice < min ||
				userChoice > max)
			{
				if (notANum)
					Console.WriteLine("You need to type in a decimal number, no less than {0}, and no greater than {1}", min, max);
				else if (userChoice < min)
					Console.WriteLine("You chose {0}, which is less than the smallest allowed number ({1})", userChoice, min);
				else if (userChoice > max)
					Console.WriteLine("You chose {0}, which is larger than the largest allowed number ({1})", userChoice, max);
			}

			return userChoice;
		}

		// A perfect place for generics.... which we're avoiding, since
		// we haven't covered them yet :)
		private int getValidUserInput(int min, int max)
		{
			int userChoice;
			bool notANum;
			while ((notANum = !Int32.TryParse(Console.ReadLine(), out userChoice)) ||
				userChoice < min ||
				userChoice > max)
			{
				if (notANum)
					Console.WriteLine("You need to type in a whole number, no less than {0}, and no greater than {1}", min, max);
				else if (userChoice < min)
					Console.WriteLine("You chose {0}, which is less than the smallest allowed number ({1})", userChoice, min);
				else if (userChoice > max)
					Console.WriteLine("You chose {0}, which is larger than the largest allowed number ({1})", userChoice, max);
			}

			return userChoice;
		}
	}
}

using System;

namespace StarterFiles
{
	class PeriodicTable
	{
		Element[] m_theElements;

		public PeriodicTable()
		{
			m_theElements = new Element[118]; // I think there are 113....changed to 118 to reflect more accurate periodic table

			// create new elements here, as needed/desired:
			m_theElements[0] = new Element(1, "Hydrogen", "H", 1.008);
			m_theElements[26] = new Element(27, "Cobalt", "Co", 58.9332);
			m_theElements[70] = new Element(71, "Lutetium", "Lu", 174.96);
			m_theElements[89] = new Element(90, "Protactinium", "Pa", 231.0359);

			// You'll need (at least) 6 more.
			// Pick them so that you can test your program

			// Don't worry about filling in more than 10, total, unless you
			// need more to test the program.
		}

		/// <summary>
		/// You might find this useful, since you're only required to fill 
		/// in 10 elements, and yet there are many more in table...
		/// </summary>
		/// <param name="atomicNum"></param>
		/// <returns>
		/// Return true if the table has data for a given element;
		/// return false otherwise
		/// </returns>
		public bool ElementHasData(int atomicNum)
		{
			// STUDENTS: YOU NEED TO WRITE YOUR CODE BELOW:
			if (m_theElements[atomicNum] != null)
				return true;
			else
				return false;

			// STUDENTS: YOU NEED TO WRITE YOUR CODE ABOVE:
		}

		public int MaxAtomicNumber
		{
			get
			{
				// STUDENTS: YOU NEED TO WRITE YOUR CODE BELOW:

				return m_theElements.Length;

				// STUDENTS: YOU NEED TO WRITE YOUR CODE ABOVE:
			}
		}

		/// <summary>
		/// This is an "indexer"
		/// </summary>
		/// <param name="atomicNum">The atomic number of the element that we're
		/// accessing.  Note that Hydrogen, the first element, is 1 (not zero).
		/// This method will take care of any 're-mapping' that needs to happen</param>
		/// <returns></returns>
		public Element getElement(int atomicNum)
		{
			int i = 2;
			// STUDENTS: YOU NEED TO WRITE YOUR CODE BELOW:

			foreach (Element e in m_theElements)
			{
				if (e != null)
				{
					if (e.getAtomicNumber() == atomicNum)
					{
						return e;
					}
				}
				else if (e == null)
				{
					Console.WriteLine("null {0}", (i));
					i++;
				}
			}
			return null;

			// STUDENTS: YOU NEED TO WRITE YOUR CODE ABOVE:
		}

		public void setElement(int atomicNum, Element e)
		{
			// STUDENTS: IMPLEMENT THIS!!
		}
	}
}


using System;

namespace StarterFiles
{
	class Element
	{
		private int atomicNum;
		private string atomicName;
		private string atomicSymbol;
		private double atomicWeight;

		public Element(int num, string name, string sym, double w)
		{
			atomicNum = num;
			atomicName = name;
			atomicSymbol = sym;
			atomicWeight = w;
		}


		// HINT: In terms of figuring out what's a transition metal, etc,
		// you should do a web search for 'Periodic table', and then see what
		// useful,scientifically relevant images you find.  Your instructor can help you
		// if you get stuck - keep in mind that while you may (or may not) find the 
		// programming-related aspects of this assignment challenging, you shouldn't be spending
		// much time on the chemistry-specific stuff.  So if you're confused about the chemistry,
		// ask!!
		public void Print()
		{
			Console.WriteLine("Name: {0}", atomicName);
			Console.WriteLine("Symbol: {0}", atomicSymbol);
			Console.WriteLine("Atomic weight: {0}", atomicWeight);
			if ((atomicNum > 20 && atomicNum < 31) || (atomicNum > 38 && atomicNum < 49) ||
				(atomicNum > 57 && atomicNum < 81) || (atomicNum > 88 && atomicNum < 113))
				Console.WriteLine("This element is a transition metal.");
			if (atomicNum > 57 && atomicNum < 81)
				Console.WriteLine("This element is a lanthanoid metal.");
			if (atomicNum > 88 && atomicNum < 113)
				Console.WriteLine("This element is an actinoid metal.");
			// STUDENTS - IMPLEMENT THIS!!
		}

		public int getAtomicNumber()
		{
			return atomicNum;
		}

		public double getWeight()
		{
			return atomicWeight;
		}
		public void setWeight(double weight)
		{
			atomicWeight = weight;
		}

		
		// Here is a great place to add any other methods that you'll need...
	}
}



Also that counter in the getElement method under the PeriodicTable class was just so I could see how far it goes before crashing. Also, you can see under that same class that there are already elements in the array that have data for them but beyond hydrogen, they do not print out.
Was This Post Helpful? 0
  • +
  • -

#4 JackOfAllTrades  Icon User is offline

  • Saucy!
  • member icon

Reputation: 6036
  • View blog
  • Posts: 23,421
  • Joined: 23-August 08

Re: foreach loop crashes because of null elements in array

Posted 11 November 2009 - 04:35 PM

Your crashing code:
m_theTable.getElement(atomicNum).Print()

If the atomic number provided does not exist, getElement returns null. You DON'T CHECK FOR THIS CONDITION, and then go on to call the Print method on a null object reference, yielding the expected Null Reference Exception.

Now, given that...what do you THINK would be the appropriate way to fix this? It ain't rocket surgery.
Was This Post Helpful? 0
  • +
  • -

#5 Momerath  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1010
  • View blog
  • Posts: 2,444
  • Joined: 04-October 09

Re: foreach loop crashes because of null elements in array

Posted 11 November 2009 - 04:39 PM

Element result = null;
foreach (Element e in m_theElements) {
    try {
        if (e.getAtomicNumber() == atomicNum) {
            result = e;
            break;
        }
    } catch (NullReferenceException e) {
        // if you wanted to do something, it would go here, but we are ignoring it.
    }
}

return result;


Exception handling information:
http://www.c-sharpco...nginCSharp.aspx
http://msdn.microsof...y/ms173160.aspx
http://www.jaggersof...ingInCSharp.htm
http://www.csharpfri...x?articleID=128
Was This Post Helpful? 0
  • +
  • -

#6 JackOfAllTrades  Icon User is offline

  • Saucy!
  • member icon

Reputation: 6036
  • View blog
  • Posts: 23,421
  • Joined: 23-August 08

Re: foreach loop crashes because of null elements in array

Posted 11 November 2009 - 05:11 PM

Cripes, actually you've already implemented a function for this...
 public bool ElementHasData(int atomicNum)

Was This Post Helpful? 0
  • +
  • -

#7 mandangalo  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 19
  • Joined: 29-July 09

Re: foreach loop crashes because of null elements in array

Posted 12 November 2009 - 12:03 AM

Quote

...most people canít learn to program: between 30% and 60% of every university computer science departmentís intake fail the first programming course


JackOfAllTrades, and that is why I am not a computer programmer :P

But I thought the statement in the printAllElements() method
 if (m_theTable.ElementHasData(atomicNum))
would take care of that.
Was This Post Helpful? 0
  • +
  • -

#8 JackOfAllTrades  Icon User is offline

  • Saucy!
  • member icon

Reputation: 6036
  • View blog
  • Posts: 23,421
  • Joined: 23-August 08

Re: foreach loop crashes because of null elements in array

Posted 12 November 2009 - 06:52 AM

Well, obviously you're working toward that end. :)

I'm not sure how I missed that call? Skimmed over it I guess? But therein lies the problem:

if (m_theTable.ElementHasData(atomicNum))
	Console.WriteLine("Cannot print data: Missing");
else
	m_theTable.getElement(atomicNum).Print();



Read that carefully. It's nicely done -- in the sense that the code reads as what it does -- but it's logically flawed.

Anyway, one thing to note is that your array is indexed by 0, so you need to subtract 1 from the atomic number before trying to access that one in the array. And do not use i <= m_theTable.Length, because the length is one greater than the max index of the array.
Was This Post Helpful? 1
  • +
  • -

#9 mandangalo  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 19
  • Joined: 29-July 09

Re: foreach loop crashes because of null elements in array

Posted 12 November 2009 - 08:33 AM

problem is solved! My loop for printAllElements() had a HUGE logical flaw pointed out to me by JackOfAllTrades that I did not catch. Where it said
for (int atomicNum = 1; atomicNum <= m_theTable.MaxAtomicNumber; atomicNum++)
			{
				if (m_theTable.ElementHasData(atomicNum))
					m_theTable.getElement(atomicNum).Print();
					
				else
				{
					Console.WriteLine("Cannot print data: Missing");
				}
			}
I should have subtracted one from atomicNum before passing it as a parameter because it was missing the 0th element in the array and trying to go beyond it in the loop. Also thanks to those who helped with suggestions to fix my getElement() method for the check to verify if there was data in and element of the array.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1