Как получить ReadOnlyCollection <T>ключей в словаре <T, S> - PullRequest
7 голосов
/ 12 ноября 2008

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

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

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

Ответы [ 6 ]

5 голосов
/ 12 ноября 2008

DrJokepu сказал, что может быть сложно реализовать оболочку для Keys Collection. Но, в данном конкретном случае, я думаю, что реализация не так сложна, потому что, как мы знаем, это оболочка только для чтения.

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

Вот краткая реализация оболочки для 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
}

Возможно, это не лучшая реализация для этих методов :), но это просто для того, чтобы доказать, что это может быть сделано.

5 голосов
/ 12 ноября 2008

Если вы действительно хотите использовать ReadOnlyCollection , проблема в том, что конструктор ReadOnlyCollection принимает IList , тогда как KeyCollection словаря является только ICollection .

Поэтому, если вы хотите обернуть KeyCollection в ReadOnlyCollection, вам нужно будет создать тип адаптера (или оболочки), реализующий IList , обертывающий KeyCollection. Так это будет выглядеть так:

var dictionary = ...;
var readonly_keys = new ReadOnlyCollection<T> (new CollectionListWrapper<T> (dictionary.Keys)
);

Не очень элегантно, тем более что KeyCollection уже является коллекцией только для чтения, и вы можете просто передать ее как ICollection :)

1 голос
/ 01 июня 2016

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

1 голос
/ 12 ноября 2008

К сожалению, вы не можете так прямо, насколько я знаю, как KeyCollection<T> не раскрывает ничего, что позволило бы вам сделать это легко.

Однако вы можете создать подкласс ReadOnlyCollection<T>, чтобы его конструктор получил словарь и переопределил соответствующие методы, чтобы он отображал элементы словаря так, как если бы они были его собственными элементами.

1 голос
/ 12 ноября 2008

Если вы используете C # 3.0 и у вас есть:

Словарь d;

Тогда

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

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

0 голосов
/ 12 ноября 2008

Это некрасиво, но это сделает это

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