Как получить ReadOnlyCollection <T> ключей в Dictionary <T, S>

Мой класс содержит Dictionary<T, S> dict, и я хочу показать ReadOnlyCollection<T> ключей. Как я могу сделать это, не копируя Dictionary<T, S>.KeyCollection dict.Keys в массив, а затем выставляя массив как ReadOnlyCollection?

Я хочу, чтобы ReadOnlyCollection был полноценной оберткой, т.е. чтобы отразить изменения в базовом словаре, и, насколько я понимаю, копирование коллекции в массив этого не сделает (а также покажется неэффективным - я на самом деле не хочу новую коллекцию, просто чтобы открыть базовую коллекцию ключей ... .). Будем очень признательны за любые идеи!

Обновлено: я использую C# 2.0, поэтому у меня нет методов расширения, таких как .ToList (легко).

Отчасти проблема в том, что ReadOnlyCollection <T> - это IList <T>, а не просто ICollection.

Ben Lings 14.09.2009 16:02

Это отстой. Я ненавижу тот факт, что ReadOnlyCollection не поддерживает IEnumerable <T> или, по крайней мере, ICollection <T>.

Shimmy Weitzhandler 02.12.2010 01:52

@Shimmy: ReadOnlyCollection <T> делает реализует IEnumerable <T>.

Joel in Gö 02.12.2010 10:52

нет, вы не можете инициализировать ReadOnlyCollection <T> ни с чего, кроме IList<T>.

Shimmy Weitzhandler 03.12.2010 05:48

Правда; Я неправильно понял, что вы имели в виду под «поддержкой». Конструктор принимает IList <T>; ReadOnlyCollection <T> реализует IList <T>, ICollection <T>, IEnumerable <T> и неуниверсальные версии интерфейсов.

Joel in Gö 03.12.2010 18:04

@Shimmy: Имя ReadOnlyCollection неудачное, ИМХО; должны были быть отдельные классы ReadOnlyListView<T> и ImmutableList<T>. Невозможно сделать доступный только для чтения IList-совместимый Посмотреть ключей словаря; можно было бы создать неизменяемый список из настоящее содержание ключей, но это отдельная операция.

supercat 20.09.2014 23:02

@Shimmy: Если бы существовал ImmutableList<T>, производный от ReadOnlyListView<T>, конструктор последнего мог бы принять IEnumerable<T>, и код, который использовался, знал бы, что он делает снимок. Перегрузка конструктора для типа ReadOnlyListView<T> для приема вещей, для которых он не может вернуть вид в реальном времени, была бы нехорошей, но разрешить коду выбирать семантику снимка или просмотра в реальном времени с помощью выбора конструктора было бы.

supercat 20.09.2014 23:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
7
6 906
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

Если вы действительно хотите использовать 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. : / Как неудобно ...

Joel in Gö 12.11.2008 23:30

@ JoelinGö: Лучшее название для ReadOnlyCollection было бы ReadOnlyListView<T>. Если бы ему дали это последнее имя, было бы очевидно, почему реализация ICollection<T> только для чтения не подходит.

supercat 20.09.2014 23:05

Это некрасиво, но это поможет

Dictionary<int,string> dict = new Dictionary<int, string>();
...
ReadOnlyCollection<int> roc = new ReadOnlyCollection<int>((new List<int>((IEnumerable<int>)dict.Keys)));

Это не отразит изменения в базовом словаре.

Joel in Gö 13.11.2008 00:10

Предполагая, что вы используете C# 3.0 и у вас есть:

Словарь <T, S> d;

Затем

ReadOnlyCollection <T> r = новая ReadOnlyCollection <T> (d.Keys.ToList ());

Вам также потребуется импортировать пространство имен System.Linq.

нет, это не отразит изменения в базовом словаре; см. ответ jb evain.

Joel in Gö 12.11.2008 23:32

В оригинальном плакате говорилось, что они использовали C# 2.0, а не 3.0.

Andy 12.11.2008 23:46

К сожалению, вы не можете сделать это прямо, насколько я знаю, поскольку 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, если не найдено.

Andre Kampling 28.10.2019 11:42

Для записи, в .NET 4.6 KeyCollection<T> реализует IReadOnlyCollection<T>, поэтому, если вы используете этот интерфейс, вы все равно можете отражать изменения в словаре, все равно получить O (1) содержит, а поскольку интерфейс ковариантен, вы можете вернуть IReadOnlyCollection<some base type>

Другие вопросы по теме