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
相关产品推荐
相关产品推荐

