K>не привлекает в виду получения клиентом прямого доступа к коллекции.
K>Существует ли простой способ сказать то, что свойство Items — возвращает readonly коллекцию? Существует ли такой интерфейс для HashSet<T>?
K>Делать правильно (реализовывать IEnumerable) привлекает, но — лень
public IEnumerable<SomeObject> Items {get {return _items.AsEnumerable(); } }
Здравствуйте, necrostaz, Вы писали:
1. HashSet<T> и так реализует IEnumerable<T>. Совершенно незачем заниматься тавтологией:
public IEnumerable<SomeObject> Items {get {return _items; } }
2. Такая реализация — небезопасна, т.к. клиент всё же может выполнить даункаст и получить доступ к коллекции.
Вот простой и надежный способ индивидуальной защиты:
public IEnumerable<SomeType> Items
{
get
{
foreach (var item in _items) yield return item;
}
}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>2. Такая реализация — небезопасна, т.к. клиент всё же может выполнить даункаст и получить доступ к коллекции.
как раз для этого AsEnumerable и предназначен
Здравствуйте, Sinclair, Вы писали:
S>2. Такая реализация — небезопасна, т.к. клиент всё же может выполнить даункаст и получить доступ к коллекции. S>Вот простой и надежный способ индивидуальной защиты:
S>public IEnumerable<SomeType> Items
S>{
S> get
S> {
S> foreach (var item in _items) yield return item;
S> }
S>}
Свойство, каждый раз возвращающее новое значение — очень плохо:
var x = o.Items;
…
var y = o.Items;
…
if(x == y) {
// сюда не попадём.
}//if
Это решение для лентяев, которые не могут написать нормальную, удобную обёртку а делов-то на десять минут.
Или же не догадываются переделать свойство на метод.
Работать с такими "свойствами" крайне не удобно. Встречаешь, приходится смотреть код, а там: ёшкин кот! xyz!
The Enumerable.AsEnumerable<TSource> method has no effect other than to change the compile-time type of source from a type that implements IEnumerable<(Of <(T>)>) to IEnumerable<(Of <(T>)>) itself.
то есть возвращается из него ровно то, что передано, и предназначен метод только для изменения статического типа у аргумента (например, когда с экземпляром IQueryable<> надо паботать как с IEnumerable<>) например в linq-выражениях или при вызове методов-расширений.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, necrostaz, Вы писали:
N>синклер прав, но тогда проще сделать .ToArray()
Ну, если вам не жалко памяти, то как бы в путь. Ну и если риск последующей рассинхронизации не пугает, то всё в порядке.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
в связи с вышесказанным _FRED_ .ToArray() тоже делать нельзя, либо делать кеширование с подсчетом изменений(а проще реализовать интерфейс) лию\бо делать метод а не свойство
Здравствуйте, Sinclair, Вы писали: S>Ну, если вам не жалко памяти, то как бы в путь. Ну и если риск последующей рассинхронизации не пугает, то всё в порядке.
а разница между foreach c yield и Array.Copy(...) ? )
Здравствуйте, necrostaz, Вы писали:
N>Здравствуйте, Sinclair, Вы писали: S>>Ну, если вам не жалко памяти, то как бы в путь. Ну и если риск последующей рассинхронизации не пугает, то всё в порядке.
N>а разница между foreach c yield и Array.Copy(...) ? )
Здравствуйте, _FRED_, Вы писали:
_FR>Это решение для лентяев, которые не могут написать нормальную, удобную обёртку а делов-то на десять минут. _FR>Или же не догадываются переделать свойство на метод.
_FR>Работать с такими "свойствами" крайне не удобно. Встречаешь, приходится смотреть код, а там: ёшкин кот! xyz!
Ок, давайте улучшим реализацию:
private EnumeratorAdapter<SomeType> _itemsWrapper;
private IEnumerable<SomeType> Items
{
get
{
return _itemsWrapper ?? (_itemsWrapper = new EnumeratorAdapter<SomeType>(GetItemsEnumerator));
}
}
private IEnumerator<SomeType> GetItemsEnumerator()
{
foreach (var b in _items)
{
yield return b;
}
}
Это в предположении, что поблизости валяется вот такой очевидный адаптер:
public class EnumeratorAdapter<T> : IEnumerable<T>
{
private Func<IEnumerator<T>> _getEnumerator;
public EnumeratorAdapter(Func<IEnumerator<T>> getEnumerator)
{
if (getEnumerator == null)
throw new ArgumentNullException("getEnumerator");
_getEnumerator = getEnumerator;
}
public IEnumerator<T> GetEnumerator()
{
return _getEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Здравствуйте, necrostaz, Вы писали:
N>а разница между foreach c yield и Array.Copy(...) ? )
Скорее вопрос в том, что между ними общего.
ToArray каждый раз выделит массив размером с количество элементов. Как верно подметил _FRED, это еще и приведет к тому, что значения свойства будут всякий раз отличаться.
А yield return имеет ленивую семантику, и всего лишь создает один экземпляр маленького класса — обертки над тем IEnumerable, который перебирается во внутреннем foreach.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, necrostaz, Вы писали:
N>>а разница между foreach c yield и Array.Copy(...) ? ) S>Скорее вопрос в том, что между ними общего. S>ToArray каждый раз выделит массив размером с количество элементов. Как верно подметил _FRED, это еще и приведет к тому, что значения свойства будут всякий раз отличаться. S>А yield return имеет ленивую семантику, и всего лишь создает один экземпляр маленького класса — обертки над тем IEnumerable, который перебирается во внутреннем foreach.
Здравствуйте, Sinclair, Вы писали:
_FR>>Это решение для лентяев, которые не могут написать нормальную, удобную обёртку а делов-то на десять минут. _FR>>Или же не догадываются переделать свойство на метод. _FR>>Работать с такими "свойствами" крайне не удобно. Встречаешь, приходится смотреть код, а там: ёшкин кот! xyz! S>Ок, давайте улучшим реализацию:
Не-а. В классе очень не сложно получить количество элементов. Почему "снаружи" имеет смысл требовать ради этого пройтись по всей коллекции? Во-вторых, мало ли пригодиться передать это свойство куда-то, что ждёт именно коллекцию? Такой адаптером круто урезает возможности для пользователя класса, без совершенно видимых на это причин. В частности, не позволяя сериализовать данные.
Нет, надо использовать нормальный read-only wrapper для ICollection<T>. Например такой (за название извиняюсь, ничего лучше в гову не пришло):
#region Using's
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
#endregion Using's
[Serializable]
[DebuggerDisplay("Count = {Count}")]
public class ReadOnlyCollection2<TItem> : ICollection<TItem>, IEnumerable<TItem>, IEnumerable
{
#region Fields
private readonly ICollection<TItem> items;
#endregion Fields
#region Constructor(s)
public ReadOnlyCollection2(ICollection<TItem> items) {
if(items == null) {
throw new ArgumentNullException("items");
}//ifthis.items = items;
}
#endregion Constructor(s)
#region Properties
protected ICollection<TItem> Items {
[DebuggerStepThrough]
get { return items; }
}
#endregion Properties
#region Methods
private Exception CreateReadOnlyException() {
return new NotSupportedException("Collection is read-only!");
}
#endregion Methods
#region ICollection<TItem> Members
void ICollection<TItem>.Add(TItem item) {
throw CreateReadOnlyException();
}
void ICollection<TItem>.Clear() {
throw CreateReadOnlyException();
}
public bool Contains(TItem item) {
return Items.Contains(item);
}
public void CopyTo(TItem[] array, int arrayIndex) {
Items.CopyTo(array, arrayIndex);
}
public int Count {
[DebuggerStepThrough]
get { return Items.Count; }
}
bool ICollection<TItem>.IsReadOnly {
[DebuggerStepThrough]
get { return true; }
}
bool ICollection<TItem>.Remove(TItem item) {
throw CreateReadOnlyException();
}
#endregion ICollection<TItem> Members
#region IEnumerable<TItem> Members
public IEnumerator<TItem> GetEnumerator() {
return Items.GetEnumerator();
}
#endregion IEnumerable<TItem> Members
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
#endregion IEnumerable Members
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали: _FR>Нет, надо использовать нормальный read-only wrapper для ICollection<T>. Например такой (за название извиняюсь, ничего лучше в гову не пришло):
Да уж. По части именования стандартных классов команде из Редмонда просто большой привет.
Мучаюсь вопросом: почему они не назвали своё творение ReadOnlyList?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, _FRED_, Вы писали: _FR>>Нет, надо использовать нормальный read-only wrapper для ICollection<T>. Например такой (за название извиняюсь, ничего лучше в гову не пришло): S>Да уж. По части именования стандартных классов команде из Редмонда просто большой привет. S>Мучаюсь вопросом: почему они не назвали своё творение ReadOnlyList?
может потому что ReadOnlyList уже есть ? ) правда в другом наймспейсе и приватный