Мой класс содержит Dictionary<T, S> dict, и я хочу показать ReadOnlyCollection<T> ключей. Как я могу сделать это, не копируя Dictionary<T, S>.KeyCollection dict.Keys в массив, а затем выставляя массив как ReadOnlyCollection?
Я хочу, чтобы ReadOnlyCollection был полноценной оберткой, т.е. чтобы отразить изменения в базовом словаре, и, насколько я понимаю, копирование коллекции в массив этого не сделает (а также покажется неэффективным - я на самом деле не хочу новую коллекцию, просто чтобы открыть базовую коллекцию ключей ... .). Будем очень признательны за любые идеи!
Обновлено: я использую C# 2.0, поэтому у меня нет методов расширения, таких как .ToList (легко).
Это отстой. Я ненавижу тот факт, что ReadOnlyCollection не поддерживает IEnumerable <T> или, по крайней мере, ICollection <T>.
@Shimmy: ReadOnlyCollection <T> делает реализует IEnumerable <T>.
нет, вы не можете инициализировать ReadOnlyCollection <T> ни с чего, кроме IList<T>.
Правда; Я неправильно понял, что вы имели в виду под «поддержкой». Конструктор принимает IList <T>; ReadOnlyCollection <T> реализует IList <T>, ICollection <T>, IEnumerable <T> и неуниверсальные версии интерфейсов.
@Shimmy: Имя ReadOnlyCollection неудачное, ИМХО; должны были быть отдельные классы ReadOnlyListView<T> и ImmutableList<T>. Невозможно сделать доступный только для чтения IList-совместимый Посмотреть ключей словаря; можно было бы создать неизменяемый список из настоящее содержание ключей, но это отдельная операция.
@Shimmy: Если бы существовал ImmutableList<T>, производный от ReadOnlyListView<T>, конструктор последнего мог бы принять IEnumerable<T>, и код, который использовался, знал бы, что он делает снимок. Перегрузка конструктора для типа ReadOnlyListView<T> для приема вещей, для которых он не может вернуть вид в реальном времени, была бы нехорошей, но разрешить коду выбирать семантику снимка или просмотра в реальном времени с помощью выбора конструктора было бы.





Если вы действительно хотите использовать ReadOnlyCollection <T>, проблема в том, что конструктор ReadOnlyCollection <T> принимает IList <T>, а KeyCollection словаря - это только ICollection <T>.
Поэтому, если вы хотите обернуть KeyCollection в ReadOnlyCollection, вам придется создать тип адаптера (или оболочки), реализующий IList <T>, обертывающий KeyCollection. Так это выглядело бы так:
var dictionary = ...;
var readonly_keys = new ReadOnlyCollection<T> (new CollectionListWrapper<T> (dictionary.Keys)
);
Не очень элегантно, тем более что KeyCollection уже является коллекцией только для чтения, и вы можете просто передать ее как ICollection <T> :)
Хм. Итак, вы говорите, что это ICollection, и он доступен только для чтения, но это не ReadOnlyCollection. : / Как неудобно ...
@ JoelinGö: Лучшее название для ReadOnlyCollection было бы ReadOnlyListView<T>. Если бы ему дали это последнее имя, было бы очевидно, почему реализация ICollection<T> только для чтения не подходит.
Это некрасиво, но это поможет
Dictionary<int,string> dict = new Dictionary<int, string>();
...
ReadOnlyCollection<int> roc = new ReadOnlyCollection<int>((new List<int>((IEnumerable<int>)dict.Keys)));
Это не отразит изменения в базовом словаре.
Предполагая, что вы используете C# 3.0 и у вас есть:
Словарь <T, S> d;
Затем
ReadOnlyCollection <T> r = новая ReadOnlyCollection <T> (d.Keys.ToList ());
Вам также потребуется импортировать пространство имен System.Linq.
нет, это не отразит изменения в базовом словаре; см. ответ jb evain.
В оригинальном плакате говорилось, что они использовали C# 2.0, а не 3.0.
К сожалению, вы не можете сделать это прямо, насколько я знаю, поскольку KeyCollection<T> не предоставляет ничего, что позволило бы вам сделать это легко.
Однако вы можете создать подкласс ReadOnlyCollection<T>, чтобы его конструктор получил сам словарь и переопределил соответствующие методы, чтобы он отображал элементы словаря, как если бы они были его собственными элементами.
DrJokepu сказал, что может быть сложно реализовать оболочку для коллекции ключей. Но в данном конкретном случае я думаю, что реализация не так уж и сложна, потому что, как мы знаем, это оболочка только для чтения.
Это позволяет нам игнорировать некоторые методы, которые в другом случае было бы сложно реализовать.
Вот быстрая реализация оболочки для Dictionary.KeyCollection:
class MyListWrapper<T, TValue> : IList<T>
{
private Dictionary<T, TValue>.KeyCollection keys;
public MyListWrapper(Dictionary<T, TValue>.KeyCollection keys)
{
this.keys = keys;
}
#region IList<T> Members
public int IndexOf(T item)
{
if (item == null)
throw new ArgumentNullException();
IEnumerator<T> e = keys.GetEnumerator();
int i = 0;
while (e.MoveNext())
{
if (e.Current.Equals(item))
return i;
i++;
}
throw new Exception("Item not found!");
}
public void Insert(int index, T item)
{
throw new NotImplementedException();
}
public void RemoveAt(int index)
{
throw new NotImplementedException();
}
public T this[int index]
{
get
{
IEnumerator<T> e = keys.GetEnumerator();
if (index < 0 || index > keys.Count)
throw new IndexOutOfRangeException();
int i = 0;
while (e.MoveNext() && i != index)
{
i++;
}
return e.Current;
}
set
{
throw new NotImplementedException();
}
}
#endregion
#region ICollection<T> Members
public void Add(T item)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(T item)
{
return keys.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
keys.CopyTo(array, arrayIndex);
}
public int Count
{
get { return keys.Count; }
}
public bool IsReadOnly
{
get { return true; }
}
public bool Remove(T item)
{
throw new NotImplementedException();
}
#endregion
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return keys.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return keys.GetEnumerator();
}
#endregion
}
Возможно, это не лучшая реализация для этих методов :) но это было просто для доказательства того, что это возможно.
Вы забыли вызвать Dispose для счетчиков в IndexOf и Indexer. Кроме того, вызов Equals для e.Current может привести к исключению NullReferenceException. Кроме того, Microsoft не хочет, чтобы в IndexOf генерировалось исключение, возвращать -1, если не найдено.
Для записи, в .NET 4.6 KeyCollection<T> реализует IReadOnlyCollection<T>, поэтому, если вы используете этот интерфейс, вы все равно можете отражать изменения в словаре, все равно получить O (1) содержит, а поскольку интерфейс ковариантен, вы можете вернуть IReadOnlyCollection<some base type>
Отчасти проблема в том, что ReadOnlyCollection <T> - это IList <T>, а не просто ICollection.