为啥我的 C# 数组在没有分配任何内容的情况下发生变化?

Posted

技术标签:

【中文标题】为啥我的 C# 数组在没有分配任何内容的情况下发生变化?【英文标题】:Why is my C# Array changing without anything being assigned to it?为什么我的 C# 数组在没有分配任何内容的情况下发生变化? 【发布时间】:2020-06-29 18:27:03 【问题描述】:

我有一个小型 winforms 程序,用于创建食谱书。该应用程序有两个窗口,但我的错误只与一个有关。

我有两个主要的类:Recipe.cs 和RecipeManager.cs,其想法是RecipeManager 包含一个Recipes 数组。 (对于这个任务,我们不允许使用 Linq 或 ArrayLists)

现在,当我填写表格并单击“添加配方”时,第一个成功运行并且列表框正确填充,但是第二次“添加配方”recipeList 数组似乎被新的完全食谱,我不知道为什么会这样。 recipeList 似乎在我的 Add 方法甚至将它添加到数组之前发生了变化!

查看相关问题的 gif:

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

食谱.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();   
        
    

【问题讨论】:

Using the free, built-in Step Debugger 调试代码比您想象的要容易。它还将帮助您了解代码的执行方式,从而帮助您编写更好的代码。 我在我的代码中添加了断点,然后慢慢地通过(并进入)。只要调用 Add ,但在 Add 有机会做任何事情之前,recipeList 就会发生变化。 @ŇɏssaPøngjǣrdenlarp 一方面,你让它变得比它需要的更复杂。数据绑定将简化显示方面,并且现代集合已经提供了您复制到查找和检查事物的所有功能。此外,您不会在调用 Add 的单击事件中创建新的配方项。然后更新道具的行正在更改最后一项并再次添加它 @Cherona 研究一下reference type,你会明白的。 【参考方案1】:
    Recipe 是一个类,这意味着它是一个 reference type. 在您的主窗体中,您的 currRecipe 实例永远不会更改。 RecipeManager 有一个数组来存储实例的引用,但不幸的是它存储了相同的实例,因为 2。 由于RecipeManager 存储了currRecipe 的同一个实例,因此对currRecipe 的任何修改都会显示N 次。

为了防止它。修改你的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();   
    

【讨论】:

以上是关于为啥我的 C# 数组在没有分配任何内容的情况下发生变化?的主要内容,如果未能解决你的问题,请参考以下文章

内存不足错误发生在堆大小高但分配大小低的情况下。为啥?

为啥我的linux中没有eth0而是enpls0

为啥在 JavaScript 中显示数组元素的上限为 100,有没有办法在没有循环或 splice() 的情况下显示所有内容? [复制]

为啥我的 Listview 没有在我的活动中显示任何内容?

为啥我不能在我的结构中打印名称?

调用方法并将返回值分配给数组时,为啥C#在调用方法时使用数组引用?