将数据与基抽象类型的具体子类关联
本文关键字:子类 关联 类型 抽象类 数据 抽象 | 更新日期: 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
}
}