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

C# WinForms DataGridView单元格验证代码优化问询

WinForms DataGridView 验证逻辑优化(替代大量if/else)

我在WinForms项目中使用DataGridView控件,支持用户通过粘贴或Excel导入方式添加数据。目前通过按钮点击事件调用ValidateGridCells方法做单元格验证,希望优化方法内的if/else嵌套写法。注:我知晓DataGridView.CellValidating事件,除非有更优实现方案,否则不想使用该事件。


优化方案:基于委托+字典的验证逻辑拆分

核心思路是把每列的验证逻辑封装成独立方法,用字典建立列头与验证方法的映射,彻底消除冗余的if/else判断,同时提升代码的可维护性。

1. 定义验证委托与映射字典

在窗体类中定义验证委托,以及存储列头与验证器的字典:

// 验证委托:参数为当前行、列索引,返回错误信息(空字符串代表验证通过)
private delegate string ColumnValidator(DataGridViewRow row, int columnIndex);

// 列头与对应验证器的映射表
private Dictionary<string, ColumnValidator> _columnValidators;

在窗体构造函数中初始化映射关系:

public YourForm()
{
    InitializeComponent();
    InitializeColumnValidators();
}

private void InitializeColumnValidators()
{
    _columnValidators = new Dictionary<string, ColumnValidator>
    {
        {"Number", ValidateNumberColumn},
        {"Account Number", ValidateAccountColumn},
        {"Status", ValidateStatusColumn},
        {"Active Date", ValidateActiveDateColumn},
        {"AC Length", ValidateAcLengthColumn}
    };
}

2. 拆分各列独立验证方法

把原if块中的逻辑拆分成单独的验证方法,每个方法只负责对应列的规则检查:

// 验证Number列
private string ValidateNumberColumn(DataGridViewRow row, int columnIndex)
{
    var cellValue = row.Cells[columnIndex].Value?.ToString();
    
    // 空值与长度校验
    if (string.IsNullOrEmpty(cellValue) || cellValue.Length != 10)
        return "号码必须为10位";
    
    // 表格内重复校验(提前缓存表格所有号码)
    var gridNumbers = dgvAddNumbers.Rows
        .Cast<DataGridViewRow>()
        .Where(r => !r.IsNewRow)
        .Select(r => r.Cells[columnIndex].Value?.ToString())
        .ToList();
    if (gridNumbers.Count(n => n == cellValue) > 1)
        return "该号码已存在于表格中";
    
    // 数据库重复校验(提前缓存数据)
    var dbNumbers = DataAccess.FetchNumbersFromDatabase(
        ConfigReader.GetValue("ConnectionStrings", "DB"),
        ConfigReader.GetValue("StoredProcedures", "FetchAllNumbers")).Result;
    if (dbNumbers.Any(n => n.ANI.ToString() == cellValue))
        return "该号码已存在于数据库中";
    
    // NPA/NXX有效性校验
    var npa = cellValue.Substring(0, 3);
    var nxx = cellValue.Substring(3, 3);
    if (!_npaList.Any(x => x.NPA == npa))
        return "NPA无效";
    if (!_nxxList.Any(x => x.NXX == nxx))
        return "NXX无效";
    
    return string.Empty;
}

// 验证Account Number列
private string ValidateAccountColumn(DataGridViewRow row, int columnIndex)
{
    var cellValue = row.Cells[columnIndex].Value?.ToString();
    
    if (string.IsNullOrEmpty(cellValue))
        return "账号缺失";
    if (!MainForm.activeAccounts.Any(a => a.AccountNumber == cellValue))
        return "账号无效";
    
    return string.Empty;
}

// 验证Status列
private string ValidateStatusColumn(DataGridViewRow row, int columnIndex)
{
    var cellValue = row.Cells[columnIndex].Value?.ToString();
    
    if (string.IsNullOrEmpty(cellValue))
        return "状态缺失";
    
    var validStatuses = Enum.GetNames(typeof(FormConstants.StatusDropDownValues));
    if (!validStatuses.Any(s => cellValue.Contains(s)))
        return "状态无效";
    
    return string.Empty;
}

// 验证Active Date列
private string ValidateActiveDateColumn(DataGridViewRow row, int columnIndex)
{
    var cellValue = row.Cells[columnIndex].Value?.ToString();
    
    if (string.IsNullOrEmpty(cellValue))
        return "激活日期缺失";
    
    if (!DateTime.TryParse(cellValue, out DateTime dt))
        return "日期格式无效";
    
    var today = DateTime.Today;
    if (dt.Date < today)
        return "日期必须为今日或未来日期";
    
    return string.Empty;
}

// 验证AC Length列
private string ValidateAcLengthColumn(DataGridViewRow row, int columnIndex)
{
    var cellValue = row.Cells[columnIndex].Value?.ToString();
    
    if (string.IsNullOrEmpty(cellValue))
        return "账号长度缺失";
    
    if (!int.TryParse(cellValue, out int length))
        return "请输入有效数字";
    
    if (length < 0 || length > 10)
        return "长度只能介于0-10之间";
    
    return string.Empty;
}

3. 重构后的ValidateGridCells主方法

private async void ValidateGridCells()
{
    // 清空所有单元格错误提示
    foreach (DataGridViewRow row in dgvAddNumbers.Rows)
    {
        if (row.IsNewRow) continue;
        foreach (DataGridViewCell cell in row.Cells)
        {
            cell.ErrorText = string.Empty;
        }
    }

    // 遍历所有列,匹配对应验证器执行校验
    foreach (DataGridViewColumn column in dgvAddNumbers.Columns)
    {
        if (_columnValidators.TryGetValue(column.HeaderText, out var validator))
        {
            foreach (DataGridViewRow row in dgvAddNumbers.Rows)
            {
                if (row.IsNewRow) continue;
                
                string errorText = validator(row, column.Index);
                row.Cells[column.Index].ErrorText = errorText;
            }
        }
    }
}

额外优化:PasteToDataGridView方法

原方法存在DataGridViewRow错误使用using块的问题,同时增加列数判断避免索引越界:

private void PasteToDataGridView(bool clearRows)
{
    var gridView = this.dgvAddNumbers;
    DataObject o = (DataObject)Clipboard.GetDataObject();
    
    if (!o.GetDataPresent(DataFormats.Text))
        return;

    if (clearRows && gridView.RowCount > 0)
        gridView.Rows.Clear();

    string pastedText = o.GetData(DataFormats.Text).ToString().TrimEnd('\r', '\n');
    string[] pastedRows = Regex.Split(pastedText, "\r\n");
    
    foreach (string pastedRow in pastedRows)
    {
        string[] pastedRowCells = pastedRow.Split('\t');
        int myRowIndex = gridView.Rows.Add();
        DataGridViewRow myDataGridViewRow = gridView.Rows[myRowIndex];
        
        // 仅填充表格已有的列,防止索引越界
        int maxColumns = Math.Min(pastedRowCells.Length, gridView.Columns.Count);
        for (int i = 0; i < maxColumns; i++)
        {
            myDataGridViewRow.Cells[i].Value = pastedRowCells[i];
        }
    }
}

优化亮点

  1. 消除冗余if/else:用字典映射替代条件判断,新增列验证只需添加方法和字典条目,符合开闭原则
  2. 逻辑解耦:每列验证独立成方法,代码更易读、易维护、易单元测试
  3. 性能提升:提前缓存表格和数据库数据,避免循环内重复遍历/查询
  4. 健壮性增强:增加格式解析容错处理,避免非法输入导致崩溃
  5. 代码简化:用Any()替代Count()>0,提升查询效率的同时简化代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 11:01:29