将数据与基抽象类型的具体子类关联

本文关键字:子类 关联 类型 抽象类 数据 抽象 | 更新日期: 2023-09-27 18:11:45

我偶然发现需要这样做几次最近与一些较低层次的框架类型的东西,我想看看是否有更好/更干净的方法来完成这一点,即,如果我错过了一些明显或聪明的东西,就像我发现[ThreadStatic]取代字典查找线程id与线程关联的数据。

我有一个基本抽象类,我们叫它Entity。每个Entity需要在构造函数中执行一组初始化操作,这取决于实际实例化的具体类。有没有一种方法,我可以做到这一点,而不做字典查找和调用this.GetType() ?

下面是一些类似于我现在的代码:
public abstract class Entity
{
    private static Dictionary<Type, Action<EntityData>> _initActions = new Dictionary<Type, Action<EntityData>>();
    private EntityData _data = new EntityData();
    protected Entity()
    {
        _initActions[this.GetType()].Invoke(_data);
    }
}
public class Employee : Entity
{
    public string Name { get; set; }
}
public class Manager : Employee
{
    public List<Employee> Subordinates { get; set; }
}

Employee构造函数和Manager构造函数需要以不同的方式初始化它们的_data字段,因为它们是不同的类型。_initActions集合在任何实例创建之前在另一个方法中初始化,我认为这在本讨论中没有任何意义。

对于框架的用户来说,我希望类的使用尽可能简单,所以我不能使用奇怪的hack,比如要求用户以某种特殊或不直观的方式在每个具体类型中重写Init方法。

泛型几乎工作,在某种意义上,我可以做像Entity<TEntity>这样的事情来获得一个特定的静态字段来存储init方法,如果我没有任何继承,但是继承需要得到支持,所以我需要TEntity子类的所有init方法的字典。

这段代码运行在一些非常低级的数据库引擎类型的场景中,以1m迭代的紧密循环运行,所以在某些情况下,摆脱字典查找确实提供了一些显着的加速(通过替换一个hack Init override实现进行测试)。

任何想法?

编辑:

我想把一些事情弄清楚。实体引擎自动设置_initAction来初始化它的_data容器。库的"用户"对这个过程一无所知,也不需要知道。我所询问的只是一种方法,以避免从基类查找字典以获取特定类型的运行时信息,但这可能是不可能的。

是的,这是微优化,但我们已经用实际查询测试过了,在一些需要实例化大型数据集的查询上,查询时间减少了15-20%。

更快的代码是这样的:

public class Employee : Entity
{
    private static EntityInitializer _initMethod = Entity.GetInitMethod(typeof(Employee));
    public string Name { get; set; }
    public Employee()
    {
        _initMethod.Invoke(this);
    }
}

这样,对Employee类型只进行一次字典查找。这并不可怕,但它需要a)在每个类中都有样板,这是我不喜欢的;b)有点容易出错,因为你必须将类型参数与当前类匹配起来,否则会发生奇怪的事情,有点像你在WPF中为依赖属性输入错误的所有者类名。有时候还行,但之后就会出现奇怪的bug,很难追踪。

归根结底是这样的:除了使用字典之外,是否有更好的方法将任意运行时数据附加到类型上,考虑到所有这些将附加此数据的类型都实现了一个公共基类?

将数据与基抽象类型的具体子类关联

您能不能直接创建一个将类型传递给的actor ?

    protected Entity(Type type)
    {
        _initActions[type].Invoke(_data);
    }
}
public class Employee : Entity
{
    private static Type mytype = typeof(Employee);
    public string Name { get; set; }
    public Employee(): base(mytype)
    { }
}

查找导致性能问题?
字典查找是0(1)和几个毫秒。
一个程序只能有这么多类。
实体仍然需要创建对象,创建一个新的EntityData,然后运行Invoke。
除了初始化实现Entity的类之外。

为什么子类的类型影响封装类的填充方式?在我看来,这似乎违反了一些OO原则。


如果子类有特定的行为,则

public abstract class Entity
{
    private readonly EntityData data = InitializeData(new EntityData());
    protected abstract void InitializeData(EntityData data);
}

似乎是基类更好的定义。指定的操作可以在子类

中定义。
Public class Employee : Entity
{
     protected override void InitializeData(EntityData data)
     {
        // Employee specific implementation here ...
     }
}

不需要Dictionary、查找甚至switch语句。不需要静态状态。这意味着与子类相关的代码必须在子类中,但这是一件好事,这是OO。


如果有必要保留更多你所拥有的东西,你可以这样做,

public abstract class Entity
{
    private readonly EntityData data;
    protected Entity(Action<EntityData> initializeData)
    {
        this.data = initializeData(new EntityData());
    }
}
public class Employee : Entity
{
    public Employee : base(SomeStaticAction)
    {
    }
}

我真的觉得你想太多了。为什么不让Entity有一个抽象的get-only属性需要被覆盖?

public abstract class Entity
{
    private static Dictionary<Type, Action<EntityData>> _initActions = 
                new Dictionary<Type, Action<EntityData>>();
    protected abstract EntityData _data { get; }
    protected Entity()
    {
        _initActions[this.GetType()].Invoke(_data);
    }
}
public class Employee : Entity
{
    public string Name { get; set; }
    protected overrides EntityData _data { 
         get { return new EntityData("Employee Stuff"); } 
    }
}
public class Manager : Employee
{
    public List<Employee> Subordinates { get; set; }
    protected overrides EntityData _data { 
         get { return new EntityData("Manager Stuff"); } 
    }
}

或者,只使用两个Init方法。

public abstract class Entity
{
    private static Dictionary<Type, Action<EntityData>> _initActions = 
                new Dictionary<Type, Action<EntityData>>();
    private void InitalizeBase() { /* do shared construction */ }
    protected abstract void Initalize();
    protected Entity()
    {
        InitalizeBase();
        Initalize();
    }
}
public class Employee : Entity
{
    public string Name { get; set; }
    protected overrides Initalize()
    {
        // Do child stuff
    }
}