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

时间:2013-05-01 15:34:47

标签: c# .net

我最近偶然发现需要使用一些较低级别的框架类型的东西,我想看看是否有更好/更清晰的方法来实现这一点,即如果我遗漏了某些东西显而易见或聪明,就像我发现[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集合在任何实例新建之前都会在另一个方法中初始化,我认为这个讨论没有任何意义。

我希望类的使用对于框架的用户保持尽可能简单,所以我不能使用奇怪的黑客,比如要求用户以某种特殊或非直观的方式覆盖每种具体类型的Init方法。 / p>

泛型几乎可以工作,从某种意义上说,如果我没有任何继承,我可以像Entity<TEntity>那样获取一个特定于TEntity的静态字段来存储init方法,但需要支持继承,所以我需要支持无论如何都需要一个TEntity子类的所有init方法的字典。

此代码在一些非常低级别的数据库引擎类型场景中以1m迭代的紧密循环运行,因此摆脱字典查找确实在某些情况下提供了一些显着的加速(通过替换为hacky Init覆盖实现进行测试)。 / p>

有什么想法吗?

编辑:

我想说清楚一些事情。实体引擎自动设置_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中为依赖项属性键入错误的所有者类名称。有时候有点工作,但是随后会出现奇怪的错误并且难以追溯。

归结为:除了使用Dictionary之外,是否有更好的方法将任意运行时数据附加到Type,考虑到所有这些附加了这些数据的类型都实现了公共基类?

3 个答案:

答案 0 :(得分:2)

你能不能创建一个你传递类型的ctor?

    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的类之外。

答案 1 :(得分:2)

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


如果某个子类有某些特殊行为,那么

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

    protected abstract void InitializeData(EntityData data);
}

似乎是基类的更好定义。可以在子类中定义specilased动作,

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)
    {
    }
}

答案 2 :(得分:0)

我真的觉得你在想这个。为什么不让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
    }
}