简体   繁体   中英

Converting Dictionary<TKey, List<TValue>> to ReadOnlyDictionary<TKey, ReadOnlyCollection<TValue>>

I have a dictionary as follows:

public enum Role { Role1, Role2, Role3, }
public enum Action { Action1, Action2, Action3, }

var dictionary = new Dictionary<Role, List<Action>>();

dictionary.Add(RoleType.Role1, new Action [] { Action.Action1, Action.Action2 }.ToList());

Now I want to be able to construct a read-only dictionary whose value type is also read-only as follows:

var readOnlyDictionary = new ReadOnlyDictionary<Role, ReadOnlyCollection<Action>>(dictionary);

The last line obviously causes a compile-time error due to the differences in TValue types.

Using a List<TValue> is also necessary since the original dictionary is programatically populated from an external source.

Is there an easy way to perform this conversion?

Converting a Dictionary to a ReadOnlyDictionary is as simple as passing the regular dictionary as a parameter to the constructor of ReadOnlyDictionary:

var myDict = new Dictionary<K, V>();

var myReadOnlyDict = new ReadOnlyDictionary<K, V>(myDictionary);

One possibility (probably suboptimal for large collections) would be to construct a new Dictionary object of the desired type (using the Enumerable.ToDictionary overload ) and use the List.AsReadOnly() extension like this:

var readOnlyDictionary = 
    new ReadOnlyDictionary<Role, ReadOnlyCollection<Action>>
        (dictionary.ToDictionary(k => k.Key, v => v.Value.AsReadOnly()));

Nearly 5 years later, here is a simple solution that adapts a dictionary, correctly handling covariance of the value's type.

/// <summary>
/// Provides a <see cref="AsReadOnly{TKey, TValue, TReadOnlyValue}(IDictionary{TKey, TValue})"/>
/// method on any generic dictionary.
/// </summary>
public static class DictionaryExtension
{
    class ReadOnlyDictionaryWrapper<TKey, TValue, TReadOnlyValue> : IReadOnlyDictionary<TKey, TReadOnlyValue>
            where TValue : TReadOnlyValue
            where TKey : notnull
    {
        private IDictionary<TKey, TValue> _dictionary;

        public ReadOnlyDictionaryWrapper( IDictionary<TKey, TValue> dictionary )
        {
            if( dictionary == null ) throw new ArgumentNullException( nameof(dictionary) );
            _dictionary = dictionary;
        }
        public bool ContainsKey( TKey key ) => _dictionary.ContainsKey( key );

        public IEnumerable<TKey> Keys => _dictionary.Keys;

        public bool TryGetValue( TKey key, [MaybeNullWhen( false )]out TReadOnlyValue value )
        {
            var r = _dictionary.TryGetValue( key, out var v );
            value = v!;
            return r;
        }

        public IEnumerable<TReadOnlyValue> Values => _dictionary.Values.Cast<TReadOnlyValue>();

        public TReadOnlyValue this[TKey key] => _dictionary[key];

        public int Count => _dictionary.Count;

        public IEnumerator<KeyValuePair<TKey, TReadOnlyValue>> GetEnumerator() => _dictionary.Select( x => new KeyValuePair<TKey, TReadOnlyValue>( x.Key, x.Value ) ).GetEnumerator();

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

    /// <summary>
    /// Creates a wrapper on a dictionary that adapts the type of the values.
    /// </summary>
    /// <typeparam name="TKey">The dictionary key.</typeparam>
    /// <typeparam name="TValue">The dictionary value.</typeparam>
    /// <typeparam name="TReadOnlyValue">The base type of the <typeparamref name="TValue"/>.</typeparam>
    /// <param name="this">This dictionary.</param>
    /// <returns>A dictionary where values are a base type of this dictionary.</returns>
    public static IReadOnlyDictionary<TKey, TReadOnlyValue> AsReadOnly<TKey, TValue, TReadOnlyValue>( this IDictionary<TKey,TValue> @this )
        where TValue : TReadOnlyValue
        where TKey : notnull
    {
        return new ReadOnlyDictionaryWrapper<TKey, TValue, TReadOnlyValue>( @this );
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM