Проблема с очисткой списка <T> - PullRequest
2 голосов
/ 16 сентября 2009

Я не знаю, почему у меня IndexOutOfRangeException, когда я очищаю System.Collections.Generic.List<T>. Имеет ли это смысл?

List<MyObject> listOfMyObject = new List<MyObject>();
listOfMyObject.Clear(); 

Ответы [ 3 ]

18 голосов
/ 16 сентября 2009

Обычно это происходит, если несколько потоков обращаются к списку одновременно. Если один поток удаляет элемент, а другой вызывает метод Clear (), это исключение может возникнуть.

«Ответ» в этом случае состоит в том, чтобы синхронизировать это надлежащим образом, блокируя все доступ к списку.


Edit:

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

Это будет простой вариант:

public class MyClassCollection
{
    // Private object for locking
    private readonly object syncObject = new object(); 

    private readonly List<MyObject> list = new List<MyObject>();
    public this[int index]
    {
        get { return list[index]; }
        set
        {
             lock(syncObject) { 
                 list[index] = value; 
             }
        }
    }

    public void Add(MyObject value)
    {
         lock(syncObject) {
             list.Add(value);
         }
    }

    public void Clear()
    {
         lock(syncObject) {
             list.Clear();
         }
    }
    // Do any other methods you need, such as remove, etc.
    // Also, you can make this class implement IList<MyObject> 
    // or IEnumerable<MyObject>, but make sure to lock each 
    // of the methods appropriately, in particular, any method
    // that can change the collection needs locking
}
7 голосов
/ 16 сентября 2009

Вы уверены, что этот код вызывает исключение? У меня

using System.Collections.Generic;

class MyObject { }

class Program {
    static void Main(string[] args) {
        List<MyObject> listOfMyObject = new List<MyObject>();
        listOfMyObject.Clear();
    }
}

и я не получаю исключения.

Ваш реальный пример более сложный? Возможно, у вас есть несколько потоков, одновременно обращающихся к списку? Можем ли мы увидеть трассировку стека?

List<T>.Clear действительно довольно просто. Использование отражателя:

public void Clear() {
    if (this._size > 0) {
        Array.Clear(this._items, 0, this._size);
        this._size = 0;
    }
    this._version++;
}

В случае, если список уже пуст, это никогда не вызовет исключения. Однако, если вы изменяете список в другом потоке, Array.Clear может вызвать исключение IndexOutOfRangeException. Таким образом, если другой поток удалит элемент из списка, то this._size (количество очищаемых элементов) будет слишком большим.

1 голос
/ 16 сентября 2009

В документации не упоминается никаких исключений, которые выдает этот метод, возможно, ваша проблема в другом месте.
List<T>.Clear

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...