1

我最近几次偶然发现需要用一些较低级别的框架类型的东西来做这件事,我想看看是否有更好/更清洁的方法来完成这个,即如果我遗漏了一些明显或聪明的东西,就像我发现[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 方法。

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

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

有任何想法吗?

编辑:

我想澄清几件事。实体引擎会自动设置 _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 中为依赖项属性键入错误的所有者类名称。Kinda 有时会起作用,但随后会出现奇怪的错误并且很难追溯。

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

4

3 回答 3

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) 和几毫秒。
一个程序只能有这么多类。
Entity 仍然需要创建对象,创建一个新的 EntityData,然后运行 ​​Invoke。
除了初始化实现实体的类。

于 2013-05-01T16:08:53.750 回答
2

为什么子类的类型会影响封装类的填充方式?对我来说,这似乎违反了一些 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 ...
     }
}

这需要 no Dictionary, lookup 甚至是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)
    {
    }
}
于 2013-05-01T16:28:47.727 回答
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
    }
}
于 2013-05-01T16:38:59.533 回答