You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#应用中Recipe对象始终存入数组索引0的问题求助

问题描述

我是编程新手,正在开发第二个C#非控制台应用,用于让用户保存食谱及其食材列表。食谱存储在Recipe对象数组(recipeList)中,但所有尝试保存的食谱都存入了recipeList[0]。我知道应该有简单的解决办法,但已经盯着代码很久仍未发现问题,目前还未编写输入验证相关代码。

食谱管理类代码

internal class RecipeManager
    {
        private Recipe[] recipeList = new Recipe[200];
        private int numOfElements = 0;
        public RecipeManager()
        {
            for(int i=0;i<200;i++)
            {
                recipeList[i] = new Recipe();
                recipeList[i].Name = string.Empty; ;
            }
        }
        public int Index(string value)//finds an index
        {
            for(int i = 0; i < 200; i++)
            {
                if (recipeList[i].Name == value)
                {
                    return i;
                }
            }
            return -1;
        }
        public void NewRecipe(Recipe recipe)//saves new recipe in empty index
        {
            recipeList[numOfElements] = recipe;
            numOfElements +=1;
        }
        public string GetRecipe(string value)//returns desired recipe
        {
            return recipeList[Index(value)].Description;
        }
        public string Test()
        {
            return recipeList[0].Name;
        }
    }

主窗体代码

public partial class Mainform : Form
    {
        static Recipe currRecipe=new Recipe();
        FormIngredients formIngredients = new FormIngredients(currRecipe);
        RecipeManager recipeManager= new RecipeManager();
        const int maxNumOfIngredients = 50;
        const int maxNumOfElements = 200;
        public Mainform()
        {
            InitializeComponent();
            InitializeGUI();
        }
        public void InitializeGUI()
        {
            btnAddRecipe.Enabled = false;
            btnClear.Enabled = false;
            btnDelete.Enabled = false;
            btnEditBegin.Enabled = false;
            btnEditFin.Enabled = false;
        }
        private void richTextBox1_TextChanged(object sender, EventArgs e)
        {

        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void btnAddRecipe_Click(object sender, EventArgs e)
        {
            currRecipe = formIngredients.Recipe;
            recipeManager.NewRecipe(currRecipe);
            lBoxRecipes.Items.Add(currRecipe.Name+" "+currRecipe.Category+" "+currRecipe.NumOfIngredients());
            formIngredients.Recipe.ClearIngredients();
            btnClear.Enabled = true;
            btnDelete.Enabled = true;
            btnEditBegin.Enabled = true;
            rTxtRecipe.Clear();
            txtName.Clear();
            cBoxCategory.SelectedIndex = -1;
            //rTxtRecipe.Text = recipeManager.Test();
        }

        private void btnAddIngredients_Click(object sender, EventArgs e)
        {
            formIngredients.ShowDialog();
            currRecipe.Name=txtName.Text;
            currRecipe.Description=rTxtRecipe.Text;
            currRecipe.SetCategory(cBoxCategory.Text);
            btnAddRecipe.Enabled = true;
        }

        private void lBoxRecipes_SelectedIndexChanged(object sender, EventArgs e)
        {
            string name=lBoxRecipes.SelectedItem.ToString().Substring(0,lBoxRecipes.SelectedItem.ToString().IndexOf(" "));
            rTxtRecipe.Text=recipeManager.GetRecipe(name);
        }

食材管理窗体代码

public partial class FormIngredients : Form
    {
        Recipe recipe=new Recipe();
        public Recipe Recipe { get { return recipe; } }
        public FormIngredients(Recipe currRecipe)
        {
            InitializeComponent();
            btnDeleteIngredient.Enabled = false;
            btnOk.Enabled = false;
            recipe= currRecipe;
        }
        private void btnAdd_Click(object sender, EventArgs e)
        {
            recipe.newIngredient(txtIngredient.Text);
            lBoxIngredients.Items.Add(txtIngredient.Text);
            txtIngredient.Clear();
            btnDeleteIngredient.Enabled = true;
            btnOk.Enabled = true;
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void txtIngredient_TextChanged(object sender, EventArgs e)
        {

        }
        private void btnDeleteIngredient_Click(object sender, EventArgs e)
        {
            recipe.RemoveIngredient(lBoxIngredients.SelectedItem.ToString());
            lBoxIngredients.Items.Remove(lBoxIngredients.SelectedItem);
        }

        private void btnOk_Click(object sender, EventArgs e)
        {
            lBoxIngredients.Items.Clear();
            this.Close();
        }

Recipe类代码

public class Recipe
    {
        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        private string description;
        public string Description
        {
            get { return description; }
            set { description = value; }
        }
        private string[] ingredients;
        private FoodCategory category;
        public FoodCategory Category
        {
            get { return category; }
        }
        public Recipe()
        {
            ingredients= new string[50];
            for(int i = 0; i < 50; i++)
            {
                ingredients[i]= string.Empty;
            }
        }
        public void SetCategory(string category)//converts and sets category for recipe
        {
            this.category = (FoodCategory)Enum.Parse(typeof(FoodCategory), category);
        }
        public void newIngredient(string ingredient)//adds ingredient to recipe
        {
            ingredients[EmptyIngredient()]= ingredient;
        }
        public int EmptyIngredient()//finds first empty slot in ingredients array
        {
            for(int i = 0; i < 50; i++)
            {
                if (ingredients[i] == string.Empty)
                {
                    return i;
                }
            }
            return -1;
        }
        public void RemoveIngredient(string ingredient)//removes ingredient
        {
            for (int i = 0; i < 50; i++)
            {
                if (ingredients[i] == ingredient)
                    ingredients[i] = string.Empty;
            }
        }
        public string NumOfIngredients()//returns number of ingredients in recipe
        {
            int num = 0;
            for (int i = 0; i < 50; i++)
            {
                if (ingredients[i] == string.Empty)
                    break;
                num++;
            }
            return num.ToString();
        }
        public void ClearIngredients()//clears all ingredients
        {
            for(int i=0;i<int.Parse(NumOfIngredients())+1;i++ )
            {
                ingredients[i]= string.Empty;
            }
        }
    }

FoodCategory是一个枚举类型

目前的问题是,尽管每次添加食谱时numOfElements值都能正常递增,但所有尝试保存的食谱都存入了数组的第一个位置,请求帮忙排查问题。


问题分析与修复方案

核心原因

你始终在复用同一个Recipe对象实例,而非每次添加新食谱时创建新实例:

  • 主窗体中定义了static Recipe currRecipe=new Recipe();,这个静态对象会被持续复用,每次添加新食谱时只是修改该对象的属性,数组中存储的都是指向这个对象的引用,导致所有数组元素看起来都是最后一次修改的内容。
  • 食材管理窗体接收的是同一个currRecipe引用,修改的始终是同一个对象的内容。

修复步骤

1. 改造主窗体的对象实例逻辑

移除currRecipe的static修饰,每次添加食材时创建新的Recipe实例:

public partial class Mainform : Form
{
    Recipe currRecipe;
    FormIngredients formIngredients;
    RecipeManager recipeManager= new RecipeManager();
    const int maxNumOfIngredients = 50;
    const int maxNumOfElements = 200;

    public Mainform()
    {
        InitializeComponent();
        InitializeGUI();
    }

    private void btnAddIngredients_Click(object sender, EventArgs e)
    {
        // 每次点击都创建新的Recipe实例
        currRecipe = new Recipe();
        currRecipe.Name = txtName.Text;
        currRecipe.Description = rTxtRecipe.Text;
        currRecipe.SetCategory(cBoxCategory.Text);
        
        formIngredients = new FormIngredients(currRecipe);
        formIngredients.ShowDialog();
        
        btnAddRecipe.Enabled = true;
    }
}

2. 优化RecipeManager构造函数(可选)

原构造函数提前创建200个空Recipe实例无必要,直接初始化数组即可:

internal class RecipeManager
{
    private Recipe[] recipeList = new Recipe[200];
    private int numOfElements = 0;

    public RecipeManager()
    {
        // 无需提前创建Recipe实例,后续添加时直接赋值
    }

    // 其他方法保持不变
}

3. 修复Recipe类的ClearIngredients方法

原方法存在清理不彻底的潜在问题,改为遍历整个食材数组:

public void ClearIngredients()
{
    for(int i=0; i<ingredients.Length; i++)
    {
        ingredients[i] = string.Empty;
    }
}

关键原理

C#中自定义类属于引用类型,赋值操作传递的是对象的内存地址。如果始终复用同一个对象,数组中所有元素都会指向该对象的地址,修改对象内容时所有数组元素都会同步变化。只有每次创建新的Recipe实例,才能保证数组中的每个元素都是独立的对象。

内容的提问来源于stack exchange,提问作者aruk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 11:25:04