将字典<TKey, List<TValue>> 转换为 ReadOnlyDictionary<TKey, ReadOnlyCollection<TValue>>
Converting Dictionary<TKey, List<TValue>> to ReadOnlyDictionary<TKey, ReadOnlyCollection<TValue>>
我有一个字典如下:
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>
也是必要的,因为原始词典是从外部源以编程方式填充的。
是否有执行此转换的简单方法?
一种可能性(对于大型集合可能不是最佳选择)是构造一个所需类型的新 Dictionary
对象(使用 Enumerable.ToDictionary overload) and use the List.AsReadOnly() extension 像这样:
var readOnlyDictionary =
new ReadOnlyDictionary<Role, ReadOnlyCollection<Action>>
(dictionary.ToDictionary(k => k.Key, v => v.Value.AsReadOnly()));
将字典转换为 ReadOnlyDictionary 就像将常规字典作为参数传递给 ReadOnlyDictionary 的构造函数一样简单:
var myDict = new Dictionary<K, V>();
var myReadOnlyDict = new ReadOnlyDictionary<K, V>(myDictionary);
将近 5 年后,这里有一个简单的解决方案,它采用字典,正确处理值类型的协方差。
/// <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 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>
也是必要的,因为原始词典是从外部源以编程方式填充的。
是否有执行此转换的简单方法?
一种可能性(对于大型集合可能不是最佳选择)是构造一个所需类型的新 Dictionary
对象(使用 Enumerable.ToDictionary overload) and use the List.AsReadOnly() extension 像这样:
var readOnlyDictionary =
new ReadOnlyDictionary<Role, ReadOnlyCollection<Action>>
(dictionary.ToDictionary(k => k.Key, v => v.Value.AsReadOnly()));
将字典转换为 ReadOnlyDictionary 就像将常规字典作为参数传递给 ReadOnlyDictionary 的构造函数一样简单:
var myDict = new Dictionary<K, V>();
var myReadOnlyDict = new ReadOnlyDictionary<K, V>(myDictionary);
将近 5 年后,这里有一个简单的解决方案,它采用字典,正确处理值类型的协方差。
/// <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 );
}
}