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

WinForms控件与变量关联优化:C# Yahtzee游戏代码改进咨询

嘿,这个场景我太熟悉了——当年我自己写Yahtzee复刻版的时候,也傻乎乎地用了一堆独立的bool变量,结果代码里到处都是重复的判断和事件处理,维护起来头都大!给你几个从简单到进阶的优化方案,帮你把代码捋得清清爽爽:

方案1:用数组/List替代独立变量(快速见效)

这是最直接的优化方式,把save1到save5这五个零散的bool变量,换成一个数组或者List<bool>:

// 初始化一个长度为5的数组,默认都是false(未保留)
private bool[] _diceSaveStates = new bool[5];
// 或者用List,灵活性稍高
// private List<bool> _diceSaveStates = new List<bool>(new bool[5]);

接下来最爽的是,你不用再写5个几乎一模一样的按钮点击事件了!给所有骰子按钮绑定同一个事件处理方法,然后通过按钮的Tag属性来区分是哪一个骰子(提前给每个按钮的Tag设为0-4的索引):

private void DiceSaveButton_Click(object sender, EventArgs e)
{
    var clickedBtn = sender as Button;
    if (clickedBtn == null) return;
    
    int diceIndex = (int)clickedBtn.Tag;
    // 切换保留状态
    _diceSaveStates[diceIndex] = !_diceSaveStates[diceIndex];
    
    // 统一更新UI,比如用颜色标记保留状态
    clickedBtn.BackColor = _diceSaveStates[diceIndex] ? Color.LightGreen : SystemColors.Control;
}

这样不管是处理保留状态,还是后续投掷时判断哪些骰子需要重新生成,都可以通过遍历数组来完成,再也不用写一堆if (save1)、if (save2)的重复代码了。

方案2:封装骰子类(面向对象进阶)

如果想让代码更易维护、扩展性更强,推荐把骰子的状态和行为封装成一个独立的Dice类。这样每个骰子的逻辑都集中在一处,以后加新功能(比如骰子滚动动画、自定义样式)也更方便:

public class Dice
{
    // 骰子当前点数
    public int CurrentValue { get; private set; }
    // 是否被保留
    public bool IsSaved { get; private set; }
    // 关联的UI按钮
    private Button _saveButton;
    private Random _rng = new Random();

    public Dice(Button saveButton)
    {
        _saveButton = saveButton;
        // 直接在类内部绑定点击事件
        _saveButton.Click += (s, e) => ToggleSaveState();
        // 初始化显示
        Roll();
    }

    // 切换保留状态并更新UI
    public void ToggleSaveState()
    {
        IsSaved = !IsSaved;
        _saveButton.BackColor = IsSaved ? Color.LightGreen : SystemColors.Control;
    }

    // 投掷骰子(仅当未保留时更新点数)
    public void Roll()
    {
        if (!IsSaved)
        {
            CurrentValue = _rng.Next(1, 7);
            _saveButton.Text = CurrentValue.ToString();
        }
    }

    // 重置保留状态(比如每轮结束后)
    public void ResetSaveState()
    {
        IsSaved = false;
        _saveButton.BackColor = SystemColors.Control;
    }
}

然后在你的Form里,用一个集合来管理所有骰子:

private List<Dice> _diceSet = new List<Dice>();

private void YahtzeeForm_Load(object sender, EventArgs e)
{
    // 把五个按钮传入,初始化骰子集合
    _diceSet.Add(new Dice(btnSave1));
    _diceSet.Add(new Dice(btnSave2));
    _diceSet.Add(new Dice(btnSave3));
    _diceSet.Add(new Dice(btnSave4));
    _diceSet.Add(new Dice(btnSave5));
}

现在投掷按钮的逻辑就变得超级简洁:

private void btnRoll_Click(object sender, EventArgs e)
{
    foreach (var dice in _diceSet)
    {
        dice.Roll();
    }
}

这种方式彻底消除了冗余代码,还让逻辑更清晰——每个骰子自己管自己的状态和行为,Form只需要负责整体流程的控制。

方案3:数据绑定(适合复杂项目)

如果你的项目后续要扩展更多功能,或者想让UI和逻辑彻底解耦,可以试试WinForms的数据绑定(虽然不如WPF灵活,但也能实现)。核心思路是创建一个ViewModel类,实现INotifyPropertyChanged,然后把UI控件和ViewModel的属性绑定起来:

public class DiceViewModel : INotifyPropertyChanged
{
    private bool _isSaved;
    private int _currentValue;
    private Random _rng = new Random();

    public bool IsSaved
    {
        get => _isSaved;
        set
        {
            _isSaved = value;
            OnPropertyChanged(nameof(IsSaved));
        }
    }

    public int CurrentValue
    {
        get => _currentValue;
        set
        {
            _currentValue = value;
            OnPropertyChanged(nameof(CurrentValue));
        }
    }

    // 切换保留状态的命令
    public ICommand ToggleSaveCommand { get; }

    public DiceViewModel()
    {
        ToggleSaveCommand = new RelayCommand(() => IsSaved = !IsSaved);
        Roll();
    }

    public void Roll()
    {
        if (!IsSaved)
        {
            CurrentValue = _rng.Next(1, 7);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

// 简单实现RelayCommand(WinForms没有内置,需要自己写)
public class RelayCommand : ICommand
{
    private readonly Action _execute;
    public event EventHandler CanExecuteChanged;

    public RelayCommand(Action execute)
    {
        _execute = execute;
    }

    public bool CanExecute(object parameter) => true;
    public void Execute(object parameter) => _execute();
}

然后在Form里创建ObservableCollection<DiceViewModel>,把按钮的Command绑定到ToggleSaveCommand,用转换器把IsSaved的bool值转换成背景色。这个方案适合大型项目,代码解耦性更好,但需要花点时间理解数据绑定的逻辑。


个人建议先从方案1入手快速优化代码,等后续需要扩展功能时再逐步过渡到方案2,这样既能快速解决当前的冗余问题,也能为未来的维护打下基础。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:02:19