为所有对象创建单个委托,而不是为每个对象创建一个委托

时间:2011-05-07 22:14:05

标签: c# delegates

我有一个对象池,每当我在池中的对象上调用Free()时,我都需要调用委托方法OnFree()。​​

在外部创建Free()并在创建池时在对象上设置。 OnFree从一个对象到另一个对象不同,有时它甚至是空的。

池中的对象继承自Poolable类。

class Poolable
{
    public Action Free();
    public Action OnFree();
}

目前,我通过这样做在继承类中创建OnFree:

class Light
{
    public Light()
    {
        // Create method to be called when Free() is called on this light.
        OnFree = () =>
        {
            DoStuffHere();
        };
    }
}

但是,这将为每个灯创建一个单独的委托,这会浪费一堆内存,尤其是当池中有数万个对象时。呃,每次调用这个构造函数时都会创建一个新的委托,对吗?

允许对象创建自己的OnFree()委托的好方法是什么,这样每个对象类型只有一个委托,而不是每个实例有一个委托?

我当然可以想到一种方式,但我希望有人能够想到一种“好”的方式 - 这种方式可以让人轻松维护。


编辑:我可以在基类中创建OnFree()委托静态,以便它以某种方式对每个继承类型是静态的吗?


编辑:阐明如何使用Pool,以及为什么Free()是委托,而不是虚拟方法。如果您能想到更好的方法,请告诉我。

public class Pool<T> where T : Poolable
{
    private int _liveCount;
    private T[] _pool;
    [...]
    public Pool(int capacity, Func<T> allocateFunction)
    {
        [...]
        // Fill pool with initial items:
        for (int i = 0; i < capacity; i++)
        {
            T item = _allocate();
            item.Free = () => Free(item);
            _pool[i] = item;
        }
    }

    /// <summary>
    /// Frees given object from this pool. Object is assumed to
    /// be in this pool.
    /// </summary>
    public void Free(Poolable obj)
    {
        obj.OnFree();

        _liveCount -= 1;
        [...]
    }
}

3 个答案:

答案 0 :(得分:2)

实际上取决于DoStuffHere()的定义位置。如果这是一个实例方法,则会在编译器生成的类型上隐式捕获this;同样可以捕获任何其他内容(在您的示例中未显示)。

在大多数正常情况下,委托实例的额外开销很小。避免传递创建委托的一种解决方法是使用参数化委托Action<SomeStateType>,可能存储在static字段中),并将状态作为单独的参数提供...但当然,你正在为国家创造一个对象!进行手动捕获的一个小优点是,您可能(它取决于确切的代码示例)将其从2个(或更多)分配(1个委托,1个或更多捕获类)减少到1个分配(您的手动捕获) ;代表被关押在静态字段上。)

另一种方式,可能会创建某些。就个人而言,直到你的分析显示它是一个瓶颈,我认为你应该放松一下 - 分配非常快,大多数时候对象将在GEN-0中收集,这非常有效。< / p>

答案 1 :(得分:2)

如何保持简单:

class Poolable
{
    public virtual void Free() { }
    public virtual void OnFree() { }  // naming not according to BCL std
}

class Light : Poolable
{
    public override void Free() { }
    ...
}
  • 您的示例显示不需要代理(通过虚拟方法)
  • 正确的封装需要事件而不是公共代理
  • 看起来你过早地进行了优化。

答案 2 :(得分:1)

如果您使用静态通用类,则每种类型都会获得一个“实例” - 这正是您所追求的。因此,使用这样的类作为特定于类型的委托的backstore,并且在每个Poolable子类的静态构造函数中初始化它们将解决您的问题。请参阅示例代码:

public class Poolable
{
    public Action Free { get; set; }
    public Action OnFree { get { return GetOnFree(); } }

    protected virtual Action GetOnFree() { throw new NotImplementedException(); }
}

public static class PoolHelper<T> where T : Poolable
{
    public static Action OnFree { get; set; }
}

public class Light : Poolable
{
    static Light()
    {
        PoolHelper<Light>.OnFree = () => 
        {
            // Define 'OnFree' for the Light type here...
            // and do so for all other other sub-classes of Poolable
        };
    }

    protected override Action GetOnFree()
    {
        return PoolHelper<Light>.OnFree;
    }
}