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;
}
}
精彩评论