Как позволить повторение по частной коллекции, но не модификации?

Если у меня есть следующий участник класса:

private List<object> obs;

и я хочу позволить обход этого списка как часть интерфейса класса, как я сделал бы это?

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

6
задан izb 19 February 2010 в 16:19
поделиться

8 ответов

Вы бы открыли его как IEnumerable , но не просто возвращали бы его напрямую:

public IEnumerable<object> Objects { get { return obs.Select(o => o); } }

Поскольку вы указали, что вам нужен только обход списка, это все тебе нужно.

У кого-то может возникнуть соблазн вернуть List напрямую как IEnumerable , но это было бы неправильно, потому что можно было бы легко проверить IEnumerable во время выполнения определите, что это List , преобразуйте его в него и измените содержимое.

Однако, используя return obs.Select (o => o); , вы возвращаете итератор по List , а не прямую ссылку на Список самого <объекта> .

Некоторые могут подумать, что это квалифицируется как «вырожденное выражение» в соответствии с разделом 7.15.2.5 Спецификации языка C #. Однако Эрик Липперт подробно объясняет, почему эта проекция не оптимизирована .

Также люди предлагают использовать метод расширения AsEnumerable . Это неверно, так как ссылочная идентичность исходного списка сохраняется. Из раздела примечаний документации:

Метод AsEnumerable (IEnumerable ) не имеет никакого эффекта, кроме как изменить тип источника во время компиляции с типа, который реализует IEnumerable в IEnumerable сам.

Другими словами, все, что он делает, это приводит параметр источника к IEnumerable , что не помогает защитить ссылочную целостность, возвращается исходная ссылка и может быть приведена обратно к List и будет использоваться для изменения списка.

13
ответ дан 8 December 2019 в 02:45
поделиться

Вы можете использовать ReadOnlyCollection или сделать копию List и вернуть ее вместо этого (учитывая штраф за производительность операции копирования). Вы также можете использовать List.AsReadOnly.

11
ответ дан 8 December 2019 в 02:45
поделиться

Это уже было сказано, но я не вижу ни одного из ответов, который был бы ясен.

Самый простой способ - просто возвращать ReadOnlyCollection

private List<object> objs;

public ReadOnlyCollection<object> Objs {
    get {
        return objs.AsReadOnly();
    }
}

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

public IEnumerable<object> Objs { 
    get {
        return objs.AsReadOnly();
    }
}

Обратите внимание, что для компиляции этого кода не обязательно вызывать AsReadOnly(). Но если вы этого не сделаете, вызывающая сторона просто вернет возвращаемое значение обратно в List и изменит ваш список.

// Bad caller code
var objs = YourClass.Objs;
var list = objs as List<object>;
list.Add(new object); // They have just modified your list.

Такая же потенциальная проблема существует и с этим решением

public IEnumerable<object> Objs { 
    get { 
        return objs.AsEnumerable(); 
    } 
}

Поэтому я бы определенно рекомендовал вызывать AsReadOnly() для вашего списка и возвращать это значение.

3
ответ дан 8 December 2019 в 02:45
поделиться

В ваш интерфейс добавьте следующую сигнатуру метода: public IEnumerable TraverseTheList()

Имплиментированный таким образом:

public IEnumerable<object> TraverseTheList()
{

    foreach( object item in obj)
    {
      yield return item;
    }


}

это позволит вам сделать следующее:

foreach(object item in Something.TraverseTheList())
{
// do something to the item
}

Возврат yield говорит компилятору построить для вас перечислитель.

2
ответ дан 8 December 2019 в 02:45
поделиться

Вы можете сделать это двумя способами:

  1. Либо преобразовав список в коллекцию Readonly:

    new System.Collections.ObjectModel.ReadOnlyCollection(this.obs)
    
    
  2. Или возвращая IEnumerable элементов:

    this.obs.AsEnumerable()
    
  3. 2
    ответ дан 8 December 2019 в 02:45
    поделиться

    Expose a ReadOnlyCollection

    1
    ответ дан 8 December 2019 в 02:45
    поделиться
    [self performSelector:@selector(printText:andMore) withObject:@"Cake" withObject:@"More Cake"];
    
    -121--1402149-

    Обычно я делаю следующее на своих HTML-страницах:

    <html>
      <head>
        <!-- head tags here -->
      </head>
      <body class="js-off">
        <script type="text/javascript">
          // Polyglot! This is valid MooTools and jQuery!
          $(document.body).addClass('js-on').removeClass('js-off');
        </script>
    
        <!-- Document markup here -->
      </body>
    </html>
    

    Использование этой техники имеет следующие преимущества:

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

    • Недопустимый код XHTML (теги < style > в < noscript > недопустимы, теги < noscript > в < head > недопустимы).

    • Стиль изменяется первым делом даже перед визуализацией остальной части страницы. Он срабатывает перед domReady , поэтому не мигает.

    Таким образом, если у вас есть виджеты с различным стилем в зависимости от того, включен JS или нет, вы можете определить свой стиль в том же месте в файле CSS.

    <style type="text/css">
      #jsWarning {
        color: red;
      }
    
      #jsConfirm {
        color: green;
      }
    
      body.js-on #jsWarning,
      body.js-off #jsConfirm {
        display: none;
      }
    </style>
    
    <div id="jsWarning">This page requires JavaScript to work properly.</div>
    <div id="jsConfirm">Congratulations, JavaScript is enabled!</div>
    
    -121--2591343-

    Интересная запись и диалог по этому вопросу: http://davybrion.com/blog/2009/10/stop-exposing-collections-already/ .

    1
    ответ дан 8 December 2019 в 02:45
    поделиться

    Вы рассматривали возможность выведения класса из System.Collections.ReadOnlyCollectionBase?

    0
    ответ дан 8 December 2019 в 02:45
    поделиться
    Другие вопросы по тегам:

    Похожие вопросы: