将 Dictionary<Key, Value> 转换为 IReadOnlyDictionary<Key, IValue>?

问题描述 投票:0回答:3

我们可以轻松做到这一点:

List<Value> list;
IReadOnlyList<IValue> List => list;

但是我们怎样才能对 IReadOnlyDictionary 做同样的事情呢?

Dictionary<Key, Value> dict;
IReadOnlyDictionary<Key, IValue> Dict => dic; //impossible
c# dictionary interface casting
3个回答
3
投票

你不能,但是以分配重新哈希为代价,你可以预测它:

IReadOnlyDictionary<string, IValue> Dict 
   => dict.ToDictionary(x => x.Key, x => (IValue)x.Value);

尽管您可能需要重新考虑您的问题。


3
投票

这是因为

IReadOnlyList
是协变的:

public interface IReadOnlyList<out T> {...}

IReadOnlyDictionary
则不是:

public interface IReadOnlyDictionary<TKey,TValue> {...}

0
投票

IReadOnlyDictionary
不变的答案引出了一个问题:为什么它必须如此。原因是,仅仅看一下它的定义方式,就无法向该接口添加任何差异: public interface IReadOnlyDictionary<TKey, TValue> : IReadOnlyCollection<KeyValuePair<TKey, TValue>> { bool ContainsKey(TKey key); bool TryGetValue(TKey key, out TValue value); TValue this[TKey key] { get; } IEnumerable<TKey> Keys { get; } IEnumerable<TValue> Values { get; } }

您不能使 
TKey

逆变 (

in
) 或
TValue
协变 (
out
)。显然
IReadOnlyDictionary<object, …>
不可能是
IReadOnlyDictionary<string, …>
,因为
IEnumerable<object> Keys
不可能是
IEnumerable<string>
至于

out TValue

,这在概念上是可行的,但是有两个部分阻止它发生:

IReadOnlyCollection<KeyValuePair<TKey, TValue>>
,因为你不能将
KeyValuePair<…, string>
转换为
KeyValuePair<…, object>
,因为非接口/委托类型根本不能有差异,和
out TValue value
。讽刺的是,
out TValue
方法参数不能与
out TValue
类型参数一起使用,因为在幕后它仍然是
ref
。理论上,这两个拦截器都可以被删除,但这是 .NET 本身必须改进的地方。
在我看来,与其将值复制到新的独立字典,不如进行仍然访问原始字典的投影。您可以使用 

struct

以零成本实现这一点:

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

public struct ProjectedValueBaseReadOnlyDictionary<TKey, TDerivedValue, TBaseValue> : IReadOnlyDictionary<TKey, TBaseValue> where TDerivedValue : TBaseValue
{
    readonly IReadOnlyDictionary<TKey, TDerivedValue> inner;

    public ProjectedValueBaseReadOnlyDictionary(IReadOnlyDictionary<TKey, TDerivedValue> inner)
    {
        this.inner = inner;
    }

    public TBaseValue this[TKey key] => inner[key];

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

    public IEnumerable<TBaseValue> Values => (inner.Values as IEnumerable<TBaseValue>) ?? inner.Values.Cast<TBaseValue>();

    public int Count => inner.Count;

    public bool ContainsKey(TKey key)
    {
        return inner.ContainsKey(key);
    }

    public IEnumerator<KeyValuePair<TKey, TBaseValue>> GetEnumerator()
    {
        foreach(var pair in inner)
        {
            yield return new KeyValuePair<TKey, TBaseValue>(pair.Key, pair.Value);
        }
    }

    public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TBaseValue value)
    {
        var result = inner.TryGetValue(key, out var value2);
        value = value2;
        return result;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

如果您随后编写像 
M<TDict>(TDict d) where TDict : IReadOnlyDictionary<Key, IValue>

这样的方法,则传入

ProjectedValueBaseReadOnlyDictionary
应该会导致优化的内联代码,只需访问
inner
字典即可。
    

© www.soinside.com 2019 - 2024. All rights reserved.