I am trying to code for an assignment at school. When I call my addElement() method and input the data to pass that data to my constructor which is used to store the data in certain elements in the array it seems to work ok. However, when I try to print the elements of the array it crashes. Since there are predefined values for certain elements in the array it prints before I try to assign any values myself yet when I try to add any then print it crashes with the NullReferenceException error and I have no idea why. Can anybody help guide me to my mistake?

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 = 1; atomicNum <= m_theTable.MaxAtomicNumber; atomicNum++)
            {
                if (m_theTable.ElementHasData(atomicNum - 1))
                    m_theTable.getElement(atomicNum).Print();
                    
                else
                {
                    Console.WriteLine("Cannot print data: Missing {0}", atomicNum);
                }
            }
            // 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)
        {
            atomicNum = atomicNum - 1;
            string atomicName;
            string atomicSym;
            double atomicWeight;
            Element e;

            if (m_theTable.ElementHasData(atomicNum))
            {
                Console.WriteLine("This element has already been defined");
                Console.WriteLine("Would you like to edit that element?");
            }
            
            else
            {
                Console.WriteLine("atomic num: {0}", atomicNum);
                Console.WriteLine("What is the atomic name of the element you would like to add?");
                atomicName = Console.ReadLine();
                Console.WriteLine("What is the atomic symbol of that element?");
                atomicSym = Console.ReadLine();
                Console.WriteLine("What is the atomic weight of that element?");
                atomicWeight = Double.Parse(Console.ReadLine());
                e = new Element(atomicNum, atomicName, atomicSym, atomicWeight);
                m_theTable.setElement(atomicNum, e);
            }
        }

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

            if (m_theTable.ElementHasData(atomicNum))
            {

            }
            // 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)
        {
            // STUDENTS: YOU NEED TO WRITE YOUR CODE BELOW:

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

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

        public void setElement(int atomicNum, Element e)
        {
            if(m_theElements[atomicNum] == null)
                m_theElements[atomicNum] = 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...
    }
}

Where is your "Main" ?

I added a new code file (called it Main.cs) and included this:

namespace StarterFiles
{
   class CStarterFiles
   {
      public static void Main(string[] args)
      {
         PeriodicTableUI ptui = new PeriodicTableUI();
         ptui.RunProgram();
      }
   }
}

The output did not crash because the constructor initializes the m_theTable.

Edited 7 Years Ago by thines01: clarification

OH haha Main is in a separate file all together. These are just the source files that actually have the more or less working code in it.

Lines 69 to 79 in your PeriodicTable class, are not ok I think, why not simply say:
return m_theElements[atomicNum];
Now the chance exists you return null which may crash your Print routine in the Element class.
And if you are allowed to do so, look up the special indexer syntax of C# and use that.

The problem is the way you are handling the atomic number...it crashes because when you add an item it has its atomic number saved incorrectly:

if (m_theTable.ElementHasData(atomicNum - 1))
      m_theTable.getElement(atomicNum).Print();

you are checking to see if 'atomicNum - 1' has data..then trying to print 'atomicNum'. A quick fix would be:

if (m_theTable.ElementHasData(atomicNum - 1))
      m_theTable.getElement(atomicNum - 1).Print();

Personally i would leave Atomic number as it should be in every part of your code except where you access the array. You are applying the -1 in a lot of places which leads to confusion and mistakes. For one thing, when creating an element, the user types a number, then the Console outputs (the number - 1)..eg
-(output)What is the atomic number you would like to add?
-(user enters) 100
-(output) atomic num: 99

I imagine you'll get better results from your lecturer if you adjust your code. The only place you should use (AtomicNum - 1) is when you access the array:

public bool ElementHasData(int atomicNum)
        {
            // STUDENTS: YOU NEED TO WRITE YOUR CODE BELOW:
            if (m_theElements[atomicNum-1] != null)
                return true;
            else
                return false;

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

this is the only place you need to make the change sicne Element 1 is in array space 0.

Hope this helps :)

While I agree with your method, it still crashes when I try to print out the array after having called the addElement() method. That's what I don't understand is somehow it's saving it wrong or something and I cannot figure out at all. Trying to print the data from the element in the array I added or printing all the elements in the array crashes it as soon as it gets to the data that I added.

Took a closer look at your code and followed the advice of Ryshad and got it working:

public Element getElement(int atomicNum)        
         {            
             // STUDENTS: YOU NEED TO WRITE YOUR CODE BELOW:  
             return m_theElements[atomicNum-1];       //Array acces so  - 1 !!!

             //foreach (Element e in m_theElements)            
             //{                
             //    if (e != null)                
             //    {                    
             //        if (e.getAtomicNumber() == atomicNum)                    
             //        {                        
             //            return e;                    
             //        }                
             //    }            
             //}            
             //return null;             
             // STUDENTS: YOU NEED TO WRITE YOUR CODE ABOVE:        
         }

As you can see Idon't go over all the elements of your array with a foreach here. It will return null if an element is not filled in anyway.
I also noticed that when you fill in a new element you ask for an atomic number and print out atomic number - 1. As Ryshad said, only use minus one when you access your array of elements.
Look over all your atomic numbers and leave -1 out when needed.

I'll give that a shot here. The only reason I was printing out the atomicNum - 1 was so that I could try to see where the information would be stored in the array.

Ryshad and ddanbe, a really appreciate your help and advice in helping me fix this error. It works like a charm now. As you can see I am in no way a computer programmer; I just have to take this class for admission requirement into my major. Thanks again!

This question has already been answered. Start a new discussion instead.