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]; } } }
优化亮点
- 消除冗余if/else:用字典映射替代条件判断,新增列验证只需添加方法和字典条目,符合开闭原则
- 逻辑解耦:每列验证独立成方法,代码更易读、易维护、易单元测试
- 性能提升:提前缓存表格和数据库数据,避免循环内重复遍历/查询
- 健壮性增强:增加格式解析容错处理,避免非法输入导致崩溃
- 代码简化:用
Any()替代Count()>0,提升查询效率的同时简化代码
内容的提问来源于stack exchange,提问作者Rodney Wilson
相关产品推荐
相关产品推荐

