将Dictionary<TKey, List<TValue>>转换为ReadOnlyDictionary<TKey, ReadOnlyCollection<TValue>>

20

我有一个如下的字典:

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());

现在我想要构建一个只读字典,其值类型也是只读的,如下所示:

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

显然,由于TValue类型的差异,最后一行会导致编译时错误。

使用 List<TValue> 也是必需的,因为原始字典是从外部源进行程序化填充的。

有没有一种简单的方法来执行此转换?


1
你知道吗?像列表这样的集合也可以使用集合初始化程序进行初始化。;P - Matías Fidemraizer
@MatíasFidemraizer:是的,我做了。但我需要在运行时构建只读集合。 - Raheel Khan
不是的,我之所以建议这样做是因为你要从数组中构建一个列表... - Matías Fidemraizer
@MatíasFidemraizer:我的错,确实有误导性。 - Raheel Khan
为什么?new List<int> { 1, 2, 3 }有什么问题吗? - Matías Fidemraizer
@MatíasFidemraizer:我是指在问题的上下文中具有误导性。我将在运行时获取值,因此无法使用数组或初始化程序进行初始化。 - Raheel Khan
3个回答

26
将字典转换为只读字典非常简单,只需将常规字典作为参数传递给 ReadOnlyDictionary 的构造函数即可:
var myDict = new Dictionary<K, V>();

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

2
这个怎么标记为正确答案的???这并没有回答只读字典中列表也是只读集合的部分。 - Xtro

11

一种可能的方式(对于大型集合来说可能不是最佳方法)是构建一个期望类型的新的Dictionary对象(使用Enumerable.ToDictionary 重载版本),然后像这样使用List.AsReadOnly() 扩展方法

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

啊!谢谢。ToDictionary 转换效果很棒。 - Raheel Khan
很高兴答案有所帮助。 - keenthinker

4

近五年后,现在有一个简单的解决方案,它使用字典并正确处理值类型的协方差。

/// <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 );
    }
}

这是正确的答案。用法如下(对于我的情况):public class SettlementResources { public IReadOnlyDictionary<Building, IReadOnlyDictionary<ResourceType, int>> BuildingStorage { get; }readonly Dictionary> BuildingStorage_ = new(); SettlementResources() { BuildingStorage = BuildingStorage_.AsReadOnly, IReadOnlyDictionary>(); }} - Xtro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接