访问泛型类中的属性

Access properties in generic class

提问人:Lucy82 提问时间:4/13/2023 最后编辑:AShLucy82 更新时间:4/14/2023 访问量:32

问:

假设我有一个这样的类:

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

如您所见,void AddEmployees 需要以 2 种不同的方式直接处理 T 类型的属性。

在第一行中,我使用动态关键字将项目添加到集合中。这使我能够编写这样的代码。

之后,我在 Collection 上使用 Linq,其中某些属性符合条件。为此,我使用反射。

这两种方式都在运行时得到解决(据我所知),这会影响性能。

我的问题是:如何以一种我可以像使用动态关键字一样编写代码的方式访问泛型类中的属性,但也可以使用 Linq 以相同的方式工作并保持所有内容匿名,以便代码在编译时解析?

C# 动态 System.Reflection 匿名类型

评论

1赞 Ralf 4/13/2023
这些是相互冲突的要求。泛型适用于在编译时已知的东西,如果你只在运行时知道它们,它不是泛型的,而是动态的。如果您的类期望 T 具有 NEW 和 START_DATE 属性,则让用作 T 的类实现具有该属性的接口,并对该接口进行泛型类型约束。然后,返回可在编译时检查的安全泛型代码,并且 LINQ 针对该接口工作。
0赞 Lucy82 4/13/2023
@Ralf我有点理解你,但不是完全理解,你能举个例子吗?

答:

2赞 mm8 4/13/2023 #1

如何以一种我可以像使用动态关键字一样编写代码的方式访问泛型类中的属性,但也可以以相同的方式使用 Linq 并保持所有内容匿名,以便代码在编译时解析?

通过在 type 参数上添加约束

public class DepartmentBase<T> : MyBase where T : IModel, 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)
    {
        T 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();
    }
}

IModel是一个接口(或类),您使用的任何类型都必须实现:DepartmentBase<T>

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

    bool? Read(string s);
    void Set(string s, bool b);
}

评论

2赞 Ralf 4/13/2023
我认为 Read 方法只是一些“工具”,向我们展示了他在 LINQ 语句中获取 NEW 属性的困难。现在您已经修复了它,LINQ 语句可以简单地使用 NEW 属性。
1赞 Lucy82 4/13/2023
@mm8,耶稣基督,这是我在阅读其他帖子时首先尝试的。但是你的最后一句话“你与 DepartmentBase<T> 一起使用的任何类型都必须实现”为我确定了它。我在错误的地方这样做 - 我在继承 DepartmentBase<T> 的类上实现了接口,而不是在类型上!...多谢!!!顺便说一句 - 不需要读取和设置,Linq 在没有它的情况下工作相同(尽管通过接口在相同的属性上):)
0赞 mm8 4/13/2023
@Ralf:没错。我显然没有很好地阅读问题中的代码,无法自己弄清楚:)
1赞 mm8 4/13/2023
对于具有类型参数的泛型类型,如果您希望能够对该类型的实例执行一些“特定”操作,例如设置或比较属性,则使用约束是正确的方法。TTT
1赞 mm8 4/13/2023
另一种选择是跳过泛型和编译时安全性,而是使用通用基类型(如 ),然后尝试在运行时强制转换参数。object