Подтвердить что ты не робот

Правильное отображение списка <T>?

Я знаю, что я не должен раскрывать List<T> в свойстве, но мне интересно, как правильно это сделать? Например, сделав это:

public static class Class1
{
    private readonly static List<string> _list;

    public static IEnumerable<string> List
    {
        get
        {
            return _list;
            //return _list.AsEnumerable<string>(); behaves the same
        }
    }

    static Class1()
    {
        _list = new List<string>();
        _list.Add("One");
        _list.Add("Two");
        _list.Add("Three");
    }
}

позволит моему абоненту просто вернуть обратно к List<T>:

    private void button1_Click(object sender, EventArgs e)
    {
        var test = Class1.List as List<string>;
        test.Add("Four"); // This really modifies Class1._list, which is bad™
    }

Итак, если мне нужен действительно неизменный List<T>, мне всегда нужно будет создать новый список? Например, кажется, что это работает (тест имеет значение null после броска):

    public static IEnumerable<string> List
    {
        get
        {
            return new ReadOnlyCollection<string>(_list);
        }
    }

Но я беспокоюсь, если есть накладные расходы на производительность, так как мой список клонируется каждый раз, когда кто-то пытается его получить?

4b9b3361

Ответ 2

Предоставление List<T> как свойства на самом деле не является корнем всего зла; особенно если это позволяет использовать ожидаемое использование, например foo.Items.Add(...).

Вы можете написать альтернативу AsEnumerable():

public static IEnumerable<T> AsSafeEnumerable<T>(this IEnumerable<T> data) {
    foreach(T item in data) yield return item;
}

Но ваша самая большая проблема на данный момент - безопасность потоков. Как статический член, у вас могут возникнуть большие проблемы, особенно если это что-то вроде ASP.NET. Даже ReadOnlyCollection по существующему списку пострадает от этого:

        List<int> ints = new List<int> { 1, 2, 3 };
        var ro = ints.AsReadOnly();
        Console.WriteLine(ro.Count); // 3
        ints.Add(4);
        Console.WriteLine(ro.Count); // 4

Так что просто обертка с AsReadOnly достаточно не, чтобы сделать ваш объект потокобезопасным; он просто защищает пользователя от добавления данных (но они все равно могут перечислять его, пока ваш другой поток добавляет данные, если вы не синхронизируете или не делаете копии).

Ответ 3

Да и Нет. Да, накладные расходы на производительность, потому что создается новый объект. Нет, ваш список не клонирован, он обернут ReadOnlyCollection.

Ответ 4

Если класс не имеет другой цели, вы можете наследовать из списка и переопределить метод add и вызвать его исключение.

Ответ 5

Вам не нужно беспокоиться о накладных расходах на клонирование: обертка коллекции с помощью ReadOnlyCollection не клонирует ее. Он просто создает обертку; если базовая коллекция изменяется, изменяется версия readonly.

Если вы беспокоитесь о создании новых оберток снова и снова, вы можете кэшировать его в отдельной переменной экземпляра.

Ответ 6

Я задал аналогичный вопрос раньше:

Исходя из этого, я бы рекомендовал вам использовать List<T> внутренне и вернуть его как Collection<T> или IList<T>. Или, если это необходимо только для перечисления, а не для добавления или антиирования, IEnumerable<T>.

По поводу того, что вы можете бросить то, что вы вернетесь к другим вещам, я бы просто сказал, не беспокойтесь. Если люди хотят использовать ваш код таким образом, чтобы он не был предназначен, они смогут так или иначе. Я также задал вопрос об этом, и я бы сказал, что единственное, что нужно сделать, это разоблачить то, что вы намереваетесь, и если люди используют его по-другому, это их проблема: p Некоторые связанные вопросы:

Ответ 7

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

Ответ 8

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

Например,

public static String[] List{
   get{
      return _List.ToArray();
   }
} 

//While using ...

String[] values = Class1.List;

foreach(string v in values){
  ...
}

// instead of calling foreach(string v in Class1.List)
// again and again, values in this context will not be
// duplicated, however values are cached instance so 
// immediate changes will not be available, but its
// thread safe
foreach(string v in values){
  ...
}