Если у меня есть следующий участник класса:
private List<object> obs;
и я хочу позволить обход этого списка как часть интерфейса класса, как я сделал бы это?
Обнародовать его не будет работать, потому что я не хочу позволять списку быть измененным непосредственно.
Вы бы открыли его как 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
и будет использоваться для изменения списка.
Вы можете использовать ReadOnlyCollection
или сделать копию List
и вернуть ее вместо этого (учитывая штраф за производительность операции копирования). Вы также можете использовать List
.
Это уже было сказано, но я не вижу ни одного из ответов, который был бы ясен.
Самый простой способ - просто возвращать 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() для вашего списка и возвращать это значение.
В ваш интерфейс добавьте следующую сигнатуру метода: 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 говорит компилятору построить для вас перечислитель.
Вы можете сделать это двумя способами:
Либо преобразовав список в коллекцию Readonly:
new System.Collections.ObjectModel.ReadOnlyCollection
Или возвращая IEnumerable элементов:
this.obs.AsEnumerable()
[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/ .
Вы рассматривали возможность выведения класса из System.Collections.ReadOnlyCollectionBase?