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

泛型类中属性的编译期访问:替代dynamic与反射的方案

编译期解析的泛型类型属性访问方案

问题背景

现有DepartmentBase<T>泛型类的AddEmployee方法中,使用dynamic关键字便捷赋值T的属性,同时通过反射结合Linq筛选修改属性。这两种方式均为运行时解析,存在性能损耗,需要一种既保留dynamic般便捷写法、支持Linq操作,又能在编译期完成所有逻辑解析的方案。

原代码如下:

public class DepartmentBase<T> : MyBase where T : new()
{
    public ObservableCollection<T> Employees
    {
       get { return _employees; }
       set { _employees = value; OnPropertyChanged(); }
    }
    private ObservableCollection<T> _employees;

    public T Selected_employee
    {
        get { return _selected_employee; }
        set { _selected_employee = value; OnPropertyChanged(); }
    }
    private T _selected_employee;
    
    public void AddEmployee(object parameter)
    { 
       //Using dynamic - each type T has those 3 properties        
       dynamic new_employee = new T();
       new_employee.NEW = true;
       new_employee.START_DATE = DateTime.Now;

       Employees.Add(new_employee);
       
       //Using reflection with Read() and Set() methods, to set each T item property "NEW" to false
       Employees.Where(a => (bool)a.Read("NEW") == true).ToList().ForEach(b => b.Set("NEW", false));

       Selected_employee = Employees.Last();
    }
}

解决方案一:接口约束(优先推荐)

核心思路是通过接口约束让泛型参数T必须实现包含所需属性的接口,使编译器在编译期就能确认T的属性成员,从而实现强类型访问,完全避免运行时解析。

步骤1:定义包含目标属性的接口

public interface IEmployee
{
    bool NEW { get; set; }
    DateTime START_DATE { get; set; }
}

步骤2:修改泛型类的约束条件

让T同时满足new()和IEmployee接口约束:

public class DepartmentBase<T> : MyBase where T : IEmployee, new()

步骤3:重构AddEmployee方法

直接以强类型方式访问属性,Linq操作也基于编译期确认的属性:

public void AddEmployee(object parameter)
{ 
    var new_employee = new T();
    new_employee.NEW = true;
    new_employee.START_DATE = DateTime.Now;

    Employees.Add(new_employee);
    
    // 直接通过强类型属性筛选并修改,编译期完成解析
    foreach (var emp in Employees.Where(a => a.NEW))
    {
        emp.NEW = false;
    }

    Selected_employee = Employees.Last();
}

方案优势

  • 编译期安全:编译器会直接检查T是否实现接口,若缺失属性会在编译阶段报错,避免运行时异常。
  • 性能无损耗:所有属性访问和Linq操作均为编译期绑定,性能与直接操作具体类型一致。
  • 写法简洁:和dynamic写法一样直观,同时保留强类型的可读性与可维护性。

解决方案二:委托缓存(适配无法修改T的场景)

如果T是第三方类型或无法直接修改其代码实现接口,可通过编译委托缓存的方式,在程序启动时一次性生成属性访问的委托,后续调用直接使用编译后的委托,性能接近强类型访问。

重构后的DepartmentBase<T>类

public class DepartmentBase<T> : MyBase where T : new()
{
    // 缓存属性访问委托,静态构造函数中仅初始化一次
    private static readonly Func<T, bool> _getNew;
    private static readonly Action<T, bool> _setNew;
    private static readonly Action<T, DateTime> _setStartDate;

    static DepartmentBase()
    {
        // 反射获取属性,并编译为委托
        var newProperty = typeof(T).GetProperty(nameof(NEW)) 
            ?? throw new InvalidOperationException("类型T必须包含'NEW'布尔属性");
        var startDateProperty = typeof(T).GetProperty(nameof(START_DATE)) 
            ?? throw new InvalidOperationException("类型T必须包含'START_DATE'DateTime属性");

        _getNew = (Func<T, bool>)Delegate.CreateDelegate(typeof(Func<T, bool>), newProperty.GetMethod);
        _setNew = (Action<T, bool>)Delegate.CreateDelegate(typeof(Action<T, bool>), newProperty.SetMethod);
        _setStartDate = (Action<T, DateTime>)Delegate.CreateDelegate(typeof(Action<T, DateTime>), startDateProperty.SetMethod);
    }

    public ObservableCollection<T> Employees
    {
       get { return _employees; }
       set { _employees = value; OnPropertyChanged(); }
    }
    private ObservableCollection<T> _employees;

    public T Selected_employee
    {
        get { return _selected_employee; }
        set { _selected_employee = value; OnPropertyChanged(); }
    }
    private T _selected_employee;
    
    public void AddEmployee(object parameter)
    { 
        var new_employee = new T();
        // 通过委托访问属性,无运行时解析开销
        _setNew(new_employee, true);
        _setStartDate(new_employee, DateTime.Now);

        Employees.Add(new_employee);
        
        // Linq结合委托筛选,编译期完成逻辑解析
        foreach (var emp in Employees.Where(emp => _getNew(emp)))
        {
            _setNew(emp, false);
        }

        Selected_employee = Employees.Last();
    }
}

方案优势

  • 低性能损耗:委托仅在静态构造函数中生成一次,后续调用为直接执行编译后的方法,性能接近强类型访问。
  • 无侵入性:不需要修改T的代码,适配第三方类型或无法修改的现有类型。
  • 编译期检查:静态构造函数中会提前验证属性是否存在,若缺失会在程序启动时抛出异常,避免运行时随机错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 10:07:48