Безопасный способ вызвать метод для каждого элемента набора, который изменяет указанный набор? - PullRequest
2 голосов
/ 27 декабря 2011

У меня определен класс, подобный этому (показаны соответствующие методы):

class ShaderProgram : IDisposable
{
    private HashSet<Shader> _shaders = new HashSet<Shader>();

    public void AttachShader(Shader shader)
    {
        GL.AttachShader(Handle, shader.Handle);
        _shaders.Add(shader);
    }

    public void DetachShader(Shader shader)
    {
        GL.DetachShader(Handle, shader.Handle);
        _shaders.Remove(shader);
    }
}

Теперь я пытаюсь выяснить, как написать метод Dispose, я думал, что смогу это сделатькак это:

    public void Dispose()
    {
        foreach(var shader in _shaders)
            DetachShader(shader);
        GL.DeleteProgram(Handle);
    }

Но теперь я подозреваю, что это небезопасно, потому что я перебираю набор и одновременно удаляю из него ... если бы это была очередь или стек, я бы просто сказал "пока нетпусто, вытолкнуть один и удалить его ", но так как это HashSet ... Я не уверен, что лучший подход.Как мне справиться с этим?

Редактировать: Дело в том, чтобы избежать дублирования кода;Я хочу позвонить ShaderProgram.DetachShader для каждого элемента.Я знаю, что могу повторить код внутри этой функции, а затем очистить весь набор в конце - это не то, что я хочу сделать.


Теперь происходит со мной, чтоЯ действительно не должен очищать HashSet вообще, не так ли?В любом случае объект ShaderProgram собирается уничтожить, все, что мне нужно сделать, это очистить все неуправляемые ресурсы, а C # GC может очистить все остальное, верно?

    public void Dispose()
    {
        foreach (var shader in _shaders)
            GL.DetachShader(Handle, shader.Handle);
        GL.DeleteProgram(Handle);
    }

Тем не менее, я до сих порхочу ответы на оригинальный вопрос.Моя цель здесь состоит не в том, чтобы решить очень простую задачу, а в том, чтобы изучить наилучший подход к этому классу задач.

Ответы [ 4 ]

2 голосов
/ 27 декабря 2011

В этом нет проблем, но, возможно, вам следует подумать об итерации набора, отсоединении шейдера от переменной GL, а затем просто очистить свой HashSet. Если вы не хотите дублировать код, который находится в вашем собственном методе отсоединения, вы должны разделить этот код и позволить ему вызываться из этих двух мест.

2 голосов
/ 27 декабря 2011

Как насчет LINQ:

while (_shaders.Any()) {
    DetachShader(_shaders.First());
}

В общем, это не сработает, потому что вы можете в конечном итоге вызвать функцию для одного и того же элемента несколько раз. Это имеет смысл, только если функция удаляет элемент из базовой коллекции.

В общем случае, когда функция не изменяет коллекцию, вы можете использовать что-то вроде этого:

while (hashset.Any()) {
    var item = hashset.First();
    Process(item);

    hashset.Remove(item);
}

Или это:

foreach (var item in hashset) {
    Process(item);
}

hashset.Clear();

Обновление: Как указано @SLaks в комментариях, вызов First() для каждого элемента в хэш-наборе при его очистке равен O (n ^ 2). Это не имеет значения для крошечных коллекций, но может иметь огромное значение, если у вас большая коллекция.

1 голос
/ 27 декабря 2011

Если ваша коллекция очень маленькая, я бы использовал:

foreach (var item in set.ToArray())
     set.Remove(item);

Другим решением будет метод RemoveWhere():

set.RemoveWhere(item =>
{
     Console.WriteLine(item);
     return true;
});
1 голос
/ 27 декабря 2011

Я не понимаю, почему кто-то с репутацией 17k спрашивает об этом. Я что-то упустил?

public void Dispose()
{
    foreach(var shader in _shaders)
        GL.DetachShader( Handle, shader.Handle );
    _shaders.Clear();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...