Создайте один делегат для всех объектов вместо одного делегата на объект - PullRequest
2 голосов
/ 08 мая 2011

У меня есть пул объектов, и мне нужно вызывать метод делегата OnFree () всякий раз, когда я вызываю Free () для объекта в пуле.

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 () статическим в базовом классе, чтобы он был статическим для унаследованного типа?


Редактировать: разъяснить, как используется пул и почему 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 ]

2 голосов
/ 08 мая 2011

Как насчет простоты:

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

class Light : Poolable
{
    public override void Free() { }
    ...
}
  • Ваш пример показывает, что нет необходимости в делегатах (через виртуальные методы)
  • для правильной инкапсуляции потребуются события вместо открытых делегатов
  • похоже, что вы оптимизируете преждевременно.
2 голосов
/ 08 мая 2011

Это на самом деле зависит от того, где определено DoStuffHere(). Если это метод экземпляра, существует неявный захват this для сгенерированного компилятором типа; аналогичным образом может быть захвачено что-либо еще (не показано в вашем примере).

В большинстве обычных случаев дополнительные издержки экземпляра делегата минимальны. Одним из обходных путей, позволяющих избежать передачи при создании делегата, является наличие параметризованного делегата (Action<SomeStateType>, возможно, сохраненного в поле static) и подача состояния в качестве отдельного параметра ... но Конечно, тогда вы создаете объект для государства! Небольшое преимущество ручного захвата состоит в том, что вы, вероятно, (это зависит от точного образца кода) сокращаете его с 2 (или более) выделений (1 делегат, 1 или более классов захвата) до 1 выделения (ваш захват вручную) ; делегат удерживается в статическом поле).

Так или иначе, вероятно, будет что-то созданное. Лично до тех пор, пока ваше профилирование не покажет, что это узкое место, я думаю, вам следует немного расслабиться - выделения выполняются очень быстро * , и в большинстве случаев объект будет собираться в GEN-0, что очень эффективно.

1 голос
/ 08 мая 2011

Если вы используете статический универсальный класс , вы получаете один «экземпляр» на тип - это именно то, что вы искали.Следовательно, использование такого класса как 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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...