Cherona
Cherona

Reputation: 814

Why is my C# Array changing without anything being assigned to it?

I have a small winforms program for a task which is to create a Recipe book. The application has two windows, but my bug pertains to only one.

I have two main classes: Recipe.cs and RecipeManager.cs, the idea is that the RecipeManager holds an array of Recipes. (For this task we weren't allowed to use Linq or ArrayLists)

Now, when I fill in the form and click "Add Recipe", the first one works successfully and the listBox populates correctly, however the second time I "Add Recipe" the recipeList array seems to get rewritten with the new Recipe entirely, and I have no idea why this is happening. The recipeList seems to change before my Add method even adds it to the array!

See gif of problem in question:

https://i.imgur.com/sIMICcG.gifv

Recipe.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace programmingTask4
{
    public class Recipe
    {
        private string[] ingredientArray;
        private string name;
        private FoodCategory category;
        private string description;
        private const int maxNumOfIngredients = 20;

       public string Name
        {
            get { return name; }
            set { name = value; }
        }
        public FoodCategory Category
        {
            get { return category; }
            set { category = value; }
        }
        public string Description
        {
            get { return description; }
            set { description = value; }
        }
        public string[] Ingredient
        {
            get { return ingredientArray; }
            set { ingredientArray = value; }
        }
        public int MaxNumOfIngredients
        {
            get { return ingredientArray.Length; }
        }

        public Recipe(int maxNumOfIngredients)
        {
            Console.WriteLine("Recipe constructor was called!");
            ingredientArray = new string[maxNumOfIngredients];
            DefaultValues();
        }
        public void DefaultValues()
        {
            for (int i = 0; i < ingredientArray.Length; i++)
            {
                ingredientArray[i] = string.Empty;
                name = string.Empty;
                category = FoodCategory.Vegetarian;
                description = string.Empty;
            }
        }
        public int FindVacantPosition()
        {
            int results;
            for (int i = 0; i < ingredientArray.Length; i++)
            {
                if(ingredientArray[i] == string.Empty)
                {
                    results = i;
                    return results;
                }
            }
            return -1;
        }
        public bool AddIngredient(string value)
        {
            bool ok;
            int next = FindVacantPosition();
            if(next >= 0)
            {
                ok = true;
                ingredientArray[next] = value;
            }
            else { 
            ok = false ;
            }
            return ok;
        }
        public bool CheckIndex(int index)
        {
            bool check = false;
            if(index <= ingredientArray.Length && index >= 0)
            {
                check = true;
            }
            return check;
        }
        public int GetCurrentNumOfIngredients()
        {
            int count = 0;
            for (int i = 0; i < ingredientArray.Length; i++)
            {
                if (!string.IsNullOrEmpty(ingredientArray[i]))
                {
                    count++;
                }
            }
            return count;
        }
        public override string ToString()
        {
            int chars = Math.Min(description.Length, 15);
            string descriptionText = description.Substring(0, chars);

            if (string.IsNullOrEmpty(descriptionText))
                descriptionText = "NO DESCRIPTION";

            string textOut = string.Format("{0, -20} {1,4} {2,-12} {3,-15}", name, GetCurrentNumOfIngredients(), category.ToString(), descriptionText);
            return textOut;
        }
        public bool ChangeIngredientAt(int index, string value)
        {
            bool bok = true;
            if (CheckIndex(index))
                ingredientArray[index] = value;
            else
                bok = false;
            return bok;
        }
        public bool DeleteIngredientAt(int index)
        {
            bool bok = true;
            if (CheckIndex(index))
                ingredientArray[index] = "NO DESCRIPTION";
            else
                bok = false;
            return bok;
        }


    }
}

RecipeManager.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace programmingTask1
{
    class RecipeManager
    {
        private Recipe[] recipeList;

        public RecipeManager(int maxNumOfElements)
        {
            Console.WriteLine("Recipe manager constructor called!");
            recipeList = new Recipe[maxNumOfElements];
        }

        private int FindVacantPosition()
        {
            for (int i = 0; i < recipeList.Length; i++)
            {
                if (recipeList[i] == null)
                {
                    Console.WriteLine("Found free position at: " + i);
                    return i;
                }

            }
            return -1;
        }
        public bool CheckIndex(int index)
        {
            bool check = false;
            if (index <= recipeList.Length && index >= 0)
            {
                check = true;
            }
            return check;
        }
        public Recipe GetRecipeAt(int index)
        {
            if (CheckIndex(index))
                return recipeList[index];
            else
                return null;
        }
        public bool Add(Recipe newRecipe)
        {
            if (newRecipe == null)
                return false;

            bool ok;
            int next = FindVacantPosition();
            if (next >= 0)
            {
                ok = true;
                Console.WriteLine("Setting recipe list at index " + next + " to " + newRecipe.ToString());
                recipeList[next] = newRecipe;

            }
            else
            {
                Console.WriteLine("No space for recipe available! " + next);
                ok = false;
            }
            return ok;
        }

        public int CurrentNumberofItems()
        {
            int num = 0;
            for (int i = 0; i < recipeList.Length; i++)
            {
                if (recipeList[i] != null)
                {
                    num++;
                }
            }
            return num;
        }
        public string[] RecipeListToString()
        {
            string[] results = new string[recipeList.Length];
            for (int i = 0; i < recipeList.Length; i++)
            {
                if (recipeList[i] != null)
                    results[i] = recipeList[i].ToString();
                else
                    results[i] = string.Empty;
            }
            return results;
        }
    }
}

FormMain.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace programmingTask4
{
    public partial class FormMain : Form
    {
        const int maxRecipe = 3;
        const int maxIngredients = 20;
        Recipe currRecipe = new Recipe(maxIngredients);
        RecipeManager recipemanager = new RecipeManager(maxRecipe);

        public FormMain()
        {
            Console.WriteLine("Form constructor was called!");
            InitializeComponent();
            InitializeGui();
        }

        public void InitializeGui()
        {
            comboBoxCategory.DataSource = Enum.GetValues(typeof(FoodCategory));
        }
        public void UpdateGUI()
        {
            //listBoxDisplay.Text = recipemanager.CurrentNumberofItems().ToString();
            //listBoxDisplay.Text = currRecipe.Ingredient.ToString();

            string[] recipeListStrings = recipemanager.RecipeListToString();
            listBoxDisplay.Items.Clear();
            listBoxDisplay.Items.AddRange(recipeListStrings);
        }

        private void buttonRecipe_Click(object sender, EventArgs e)//add recipe button
        {
            currRecipe.Category = (FoodCategory)comboBoxCategory.SelectedIndex;
            currRecipe.Name = textBoxRecipeName.Text.Trim();
            currRecipe.Description = richTextBoxDescription.Text.Trim();

            Console.WriteLine("Adding recipe: " + currRecipe.ToString());
            bool result = recipemanager.Add(currRecipe);
            Console.WriteLine("Result was " + result + " for adding to recipe list");
            UpdateGUI();

            currRecipe.DefaultValues();   
        }
    }
}

Upvotes: 0

Views: 143

Answers (1)

Louis Go
Louis Go

Reputation: 2578

  1. Recipe is a class, which mean it's a reference type.
  2. In your main form, your currRecipe instance is never changed.
  3. RecipeManager has an array to store references of instance but unfortunately it stores the same instance because of 2.
  4. Since RecipeManager stores the same instance of currRecipe, any modification on currRecipe would display N times.

To prevent it. Modify your buttonRecipe_Click

    private void buttonRecipe_Click(object sender, EventArgs e)//add recipe button
    {
        currRecipt = new Recipe(maxIngredients);
        currRecipe.Category = (FoodCategory)comboBoxCategory.SelectedIndex;
        currRecipe.Name = textBoxRecipeName.Text.Trim();
        currRecipe.Description = richTextBoxDescription.Text.Trim();

        Console.WriteLine("Adding recipe: " + currRecipe.ToString());
        bool result = recipemanager.Add(currRecipe);
        Console.WriteLine("Result was " + result + " for adding to recipe list");
        UpdateGUI();

        // No need to reset it, use new one everytime.
        //currRecipe.DefaultValues();   
    }

Upvotes: 1

Related Questions