C Sharp 属性验证
外观
C#属性验证[编辑 | 编辑源代码]
介绍[编辑 | 编辑源代码]
属性验证是C#中确保对象状态合法性的重要机制,通过验证逻辑限制属性值的有效范围。当属性被赋值时,系统会执行预定义的检查规则,若值不符合要求则抛出异常或采取其他处理方式。这种技术广泛应用于数据完整性保护、用户输入验证和业务规则强制执行等场景。
C#提供多种实现属性验证的方式:
- 字段封装与setter验证
- 特性注解(Data Annotations)
- INotifyDataErrorInfo接口
- IDataErrorInfo接口
- 自定义验证框架集成
基础验证模式[编辑 | 编辑源代码]
字段封装验证[编辑 | 编辑源代码]
最基础的验证方式是在属性setter中编写条件逻辑:
public class Product
{
private decimal _price;
public decimal Price
{
get => _price;
set
{
if (value < 0)
throw new ArgumentOutOfRangeException("价格不能为负数");
_price = value;
}
}
}
示例输出:
Product p = new Product(); p.Price = -10; // 抛出ArgumentOutOfRangeException
特性注解验证[编辑 | 编辑源代码]
System.ComponentModel.DataAnnotations命名空间提供验证特性:
using System.ComponentModel.DataAnnotations;
public class User
{
[Required(ErrorMessage = "用户名必填")]
[StringLength(50, MinimumLength = 3)]
public string Username { get; set; }
[Range(18, 120, ErrorMessage = "年龄必须在18-120之间")]
public int Age { get; set; }
}
高级验证技术[编辑 | 编辑源代码]
自定义验证特性[编辑 | 编辑源代码]
创建继承自ValidationAttribute的类实现复杂规则:
public class EvenNumberAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
if (value is int number && number % 2 == 0)
return ValidationResult.Success;
return new ValidationResult("必须输入偶数");
}
}
public class InventoryItem
{
[EvenNumber]
public int ShelfQuantity { get; set; }
}
跨属性验证[编辑 | 编辑源代码]
通过类级别特性实现多属性关联验证:
[AttributeUsage(AttributeTargets.Class)]
public class DateRangeValidAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
var model = value as Event;
return model.StartDate < model.EndDate;
}
}
[DateRangeValid(ErrorMessage = "结束日期必须晚于开始日期")]
public class Event
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
验证流程模型[编辑 | 编辑源代码]
实际应用案例[编辑 | 编辑源代码]
电商系统价格验证[编辑 | 编辑源代码]
public class Product
{
private decimal _originalPrice;
private decimal _discountedPrice;
public decimal OriginalPrice
{
get => _originalPrice;
set => _originalPrice = value >= 0 ? value :
throw new ArgumentException("原价不能为负");
}
public decimal DiscountedPrice
{
get => _discountedPrice;
set
{
if (value < 0)
throw new ArgumentException("折扣价不能为负");
if (value > OriginalPrice)
throw new ArgumentException("折扣价不能高于原价");
_discountedPrice = value;
}
}
}
用户注册表单验证[编辑 | 编辑源代码]
结合WPF/MVVM模式的典型实现:
public class RegistrationViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
{
private string _email;
private readonly Dictionary<string, List<string>> _errors = new();
[Required]
[EmailAddress]
public string Email
{
get => _email;
set
{
_email = value;
ValidateEmail();
OnPropertyChanged();
}
}
private void ValidateEmail()
{
ClearErrors(nameof(Email));
if (string.IsNullOrWhiteSpace(Email))
AddError(nameof(Email), "邮箱地址不能为空");
else if (!new EmailAddressAttribute().IsValid(Email))
AddError(nameof(Email), "邮箱格式无效");
}
// INotifyDataErrorInfo实现...
}
数学公式验证示例[编辑 | 编辑源代码]
对于需要数学验证的场景(如几何图形类):
对应代码实现:
public class Rectangle
{
private double _width;
private double _height;
public double Width
{
get => _width;
set => _width = value > 0 ? value :
throw new ArgumentException("宽度必须大于0");
}
public double Height
{
get => _height;
set => _height = value > 0 ? value :
throw new ArgumentException("高度必须大于0");
}
public double Area => Width * Height;
}
最佳实践[编辑 | 编辑源代码]
1. 尽早验证:在数据进入系统时立即验证 2. 明确错误信息:提供可操作的错误提示 3. 分层验证:
* 前端:即时反馈 * 后端:最终防线
4. 避免重复验证:合理设计验证层级 5. 性能考虑:复杂验证延迟到必要时执行
常见问题[编辑 | 编辑源代码]
Q:属性验证与构造函数验证有何区别? A:构造函数验证确保对象创建时的初始状态合法,属性验证则维护对象整个生命周期的状态合法性。两者通常需要配合使用。
Q:何时该抛出异常而不是返回验证结果? A:对于程序逻辑错误(如null引用)应抛出异常;对于预期的业务规则违反(如用户输入无效)通常返回验证结果更合适。