遍历Dictionary<int, int[]>得到非预期结果的C#技术问询
桌游主持辅助应用权重计算模块问题修复
问题描述
使用C#和Visual Studio开发桌游主持辅助应用时,权重计算模块出现异常:点击按钮后本应生成单一宝藏结果,实际却得到3-5组结果的累加值。按钮仅绑定一次点击事件,预期逻辑为掷出d100后,匹配第一个满足d100 ≤ 字典键值的条目并执行对应骰子滚动,但实际代码遍历了所有符合条件的键并累加结果。
相关代码
private void challenge1_Click(object sender, EventArgs e) { Dictionary<int, int[]> weights = new Dictionary<int, int[]>() { { 30, new int[]{5,0,0,0,0}}, { 60, new int[]{0,4,0,0,0}}, { 70, new int[]{0,0,3,0,0}}, { 95, new int[]{0,0,0,3,0}}, { 100,new int[]{0,0,0,0,1}} }; Random one = new Random(); Random two = new Random(); int d100 = RollDice(100, one); int[] results = IndividualTreasure(weights, d100); TreasureResults(results); } private int[] IndividualTreasure(Dictionary<int, int[]> weights, int d100) { int cp = 0, sp = 0, gp = 0, ep = 0, pp = 0; Random num = new Random(); foreach (KeyValuePair<int, int[]> entry in weights) { if (d100 <= entry.Key) { for (int i = 0; i < entry.Value.Length; i++) { if (entry.Value[i] > 0) { switch (i) { case 0: for (int j = 0; j < entry.Value[i]; j++) { int randNum = RollDice(6, num); cp += randNum; } break; case 1: for (int j = 0; j < entry.Value[i]; j++) { int randNum = RollDice(6, num); sp += randNum; } break; case 2: for (int j = 0; j < entry.Value[i]; j++) { int randNum = RollDice(6, num); gp += randNum; } break; case 3: for (int j = 0; j < entry.Value[i]; j++) { int randNum = RollDice(6, num); ep += randNum; } break; case 4: for (int j = 0; j < entry.Value[i]; j++) { int randNum = RollDice(6, num); pp += randNum; } break; } } } } } int[] result = new int[5] { cp, sp, gp, ep, pp }; return result; } private void TreasureResults(int[] results) { int cp = results[0]; int sp = results[1]; int gp = results[2]; int ep = results[3]; int pp = results[4]; lbl_cp_amount.Text = cp.ToString(); lbl_sp_amount.Text = sp.ToString(); lbl_gp_amount.Text = gp.ToString(); lbl_ep_amount.Text = ep.ToString(); lbl_pp_amount.Text = pp.ToString(); }
问题根源
- 字典遍历逻辑错误:
Dictionary默认不保证排序,当前代码会遍历所有满足d100 ≤ entry.Key的条目并累加结果。例如掷出d100=20时,30、60、70、95、100这些键都符合条件,导致所有对应宝藏类型的骰子结果被累加,出现多组结果。 - Random实例重复创建:短时间内多次创建
Random实例(one、two、num)会因系统时钟种子相同,导致生成的随机数重复或不符合预期。
修复方案
修复后的代码
// 类级别全局Random实例,确保随机性正常 private Random _random = new Random(); private void challenge1_Click(object sender, EventArgs e) { Dictionary<int, int[]> weights = new Dictionary<int, int[]>() { { 30, new int[]{5,0,0,0,0}}, { 60, new int[]{0,4,0,0,0}}, { 70, new int[]{0,0,3,0,0}}, { 95, new int[]{0,0,0,3,0}}, { 100,new int[]{0,0,0,0,1}} }; int d100 = RollDice(100, _random); int[] results = IndividualTreasure(weights, d100); TreasureResults(results); } private int[] IndividualTreasure(Dictionary<int, int[]> weights, int d100) { int cp = 0, sp = 0, gp = 0, ep = 0, pp = 0; // 将字典按键升序排序,确保从最低阈值开始匹配 var sortedWeights = weights.OrderBy(kv => kv.Key); foreach (KeyValuePair<int, int[]> entry in sortedWeights) { if (d100 <= entry.Key) { for (int i = 0; i < entry.Value.Length; i++) { if (entry.Value[i] > 0) { switch (i) { case 0: for (int j = 0; j < entry.Value[i]; j++) cp += RollDice(6, _random); break; case 1: for (int j = 0; j < entry.Value[i]; j++) sp += RollDice(6, _random); break; case 2: for (int j = 0; j < entry.Value[i]; j++) gp += RollDice(6, _random); break; case 3: for (int j = 0; j < entry.Value[i]; j++) ep += RollDice(6, _random); break; case 4: for (int j = 0; j < entry.Value[i]; j++) pp += RollDice(6, _random); break; } } } // 找到匹配条目后立即退出循环,避免处理后续条目 break; } } return new int[5] { cp, sp, gp, ep, pp }; } private void TreasureResults(int[] results) { lbl_cp_amount.Text = results[0].ToString(); lbl_sp_amount.Text = results[1].ToString(); lbl_gp_amount.Text = results[2].ToString(); lbl_ep_amount.Text = results[3].ToString(); lbl_pp_amount.Text = results[4].ToString(); }
关键修复点
- 全局Random实例:避免短时间内重复创建
Random导致的随机数重复问题,统一使用类级别实例生成所有随机数。 - 排序+终止循环:将字典按键升序排序后,找到第一个满足条件的条目就执行
break终止遍历,确保只生成一组宝藏结果,符合预期逻辑。
内容的提问来源于stack exchange,提问作者IllusionIndustries
相关产品推荐
相关产品推荐

