开发者

Create a single delegate for all objects instead of one delegate per object

开发者 https://www.devze.com 2023-03-03 21:18 出处:网络
I have an object pool, and I need to call a delegate method OnFree(), whenever I call Free() on an object in the pool.

I have an object pool, and I need to call a delegate method OnFree(), whenever I call Free() on an object in the pool.

Free() is created externally and set on the object when the pool is created. OnFree differs fr开发者_运维知识库om one object to another, and sometimes it is even null.

Objects in the pool inherit from the Poolable class.

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

Currently I create OnFree in the inheriting class by doing this:

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

However, this will create a separate delegate for each light, which wastes a bunch of memory especially when there are tens of thousands of objects in the pool. Er, it does create a new delegate every time this constructor is called, right?

What is a good way to allow objects to create their own OnFree() delegate, so that there is only one delegate per object type, instead of one delegate per instance?

I can think of a way of course, but I'm hoping someone can think of a "good" way -- something that allows easy maintainability.


Edit: Can I make the OnFree() delegate static in the base class, so that it is static per inherited type somehow?


Edit: To clarify how Pool is used, and why Free() is a delegate, not a virtual method. Please let me know if you can think of a better way to do this.

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;
        [...]
    }
}


How about keeping it simple:

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

class Light : Poolable
{
    public override void Free() { }
    ...
}
  • your example shows no need for delegates (over virtual methods)
  • proper encapsulation would require events instead of public delegates
  • looks like you are optimizing prematurely.


It actually depends on where DoStuffHere() is defined. If this is an instance method, there is an implicit capture of this onto a compiler-generated type; likewise anything else (not shown in your example) might be captured.

In most normal cases the extra overhead of a delegate instance is minimal. One workaround to avoid passing creating a delegate is to have a parameterised delegate (an Action<SomeStateType>, perhaps stored in a static field), and feed the state in as a separate parameter... but of course, then you are creating an object for the state! The slight advantage of doing a manual capture is that you are probably (it depends on the exact code sample) reducing it from 2 (or more) allocations (1 delegate, 1-or-more capture classes) to 1 allocation (your manual capture; the delegate being held on a static field).

One way of another, there is likely going to be something created. Personally, until your profiling shows it is a bottleneck, I think you should relax a bit - allocations are very fast, and most times the object will be collected in GEN-0, which is very efficient.


If you use a static generic class you get one "instance" per type - which is exactly what you were after. Hence, using such a class as the backstore for your type-specific delegates, and initialize them in the static constructor of each Poolable sub-class would solve your problem. See the sample code:

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;
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消