跳转到内容

C Sharp 属性验证

来自代码酷
Admin留言 | 贡献2025年4月29日 (二) 18:40的版本 (Page creation by admin bot)

(差异) ←上一版本 | 已核准修订 (差异) | 最后版本 (差异) | 下一版本→ (差异)

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; }
}

验证流程模型[编辑 | 编辑源代码]

graph TD A[属性赋值] --> B{是否满足验证规则?} B -->|是| C[更新字段值] B -->|否| D[触发验证错误] D --> E[抛出异常/收集错误]

实际应用案例[编辑 | 编辑源代码]

电商系统价格验证[编辑 | 编辑源代码]

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实现...
}

数学公式验证示例[编辑 | 编辑源代码]

对于需要数学验证的场景(如几何图形类):

面积={合法当 x>0 且 y>0非法其他情况

对应代码实现:

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引用)应抛出异常;对于预期的业务规则违反(如用户输入无效)通常返回验证结果更合适。