我们可以轻松做到这一点:
List<Value> list;
IReadOnlyList<IValue> List => list;
但是我们怎样才能对 IReadOnlyDictionary 做同样的事情呢?
Dictionary<Key, Value> dict;
IReadOnlyDictionary<Key, IValue> Dict => dic; //impossible
你不能,但是以分配和重新哈希为代价,你可以预测它:
IReadOnlyDictionary<string, IValue> Dict
=> dict.ToDictionary(x => x.Key, x => (IValue)x.Value);
尽管您可能需要重新考虑您的问题。
这是因为
IReadOnlyList
是协变的:
public interface IReadOnlyList<out T> {...}
而
IReadOnlyDictionary
则不是:
public interface IReadOnlyDictionary<TKey,TValue> {...}
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
字典即可。